前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >vue 随记(6):构建的艺术

vue 随记(6):构建的艺术

作者头像
一粒小麦
发布于 2020-07-28 07:50:37
发布于 2020-07-28 07:50:37
1K00
代码可运行
举报
文章被收录于专栏:一Li小麦一Li小麦
运行总次数:0
代码可运行

vite的构建艺术

Vite 是一个由原生 ESM 驱动的 Web 开发构建工具。在开发环境下基于浏览器原生 ES imports 开发,在生产环境下基于 Rollup 打包。 ——https://zhuanlan.zhihu.com/p/150083887?from_voters_page=true

1. 打包vs解析

做过vue项目的人都知道,当项目越变越大,或者变成多页面应用时,热更新打包速度奇慢无比,每次保存都要几分钟。

尤雨溪在B站直播提到他最近在做的这工具 vite[1] ,一个实验性的no build的vue开发服务器(这个小工具可以支持热更新,且不用预编译)。它的特性有:

•基于浏览器原生JS module功能(补白阅读:在浏览器中使用javascript module(译)),因而有更快的冷启动和热更新,整体速度与模块数量无关(无论项目多大,都是O(1)复杂度)•没有打包的过程,源码直接传输给浏览器,使用原生的 <script module> 语法进行引入,开发服务器拦截请求和对需要转换的代码进行转换。•实现了真正的按需编译。打开哪个页面,就解析哪些模块。•生产环境提供了 vite build 脚本进行打包,它基于 rollup 进行打包

vite构建的简单过程可以看到如下:

此过程可以理解为“只解析,不打包”。理论上支持react等任意前端开发框架。作者甚至在社交网络直言:他以后再不想用 webpack 了。

但是,因为JS module是“现代浏览器”支持功能,对于远古浏览器是不支持的。因此,只建议在开发环境下使用。对于生产环境,还是只能走打包那套。

2. mini版的实现

项目需求:基于现代浏览器实现一个mini版的vite工具。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
npm init  -y

在项目中新建一个index.html。通过script type="module"引入main.js文件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>vite-mini</title>
</head>
<body>
  <!-- 单页面容器 -->
  <div id="app"></div>
  <script type="module" src="src/main.js"></script>
</body>
</html>

新建src/main.js,直接输入console.log('main.js'),直接打开index.html,发现这种操作被同源策略阻止了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Access to script at 'file:///Users/dangjingtao/Desktop/vite-min/src/main.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
index.html:12 GET file:///Users/dangjingtao/Desktop/vite-min/src/main.js net::ERR_FAILED

2.1 启服务

所以还是得起服务器。此时可以选用koa启服务。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
npm i koa -S

新建server.js简单写一个服务:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//  ./server.js
// 用最传统的方式
const fs = require('fs');
const path = require('path');
const Koa = require('koa');

const app = new Koa();

app.use(async ctx => {
  const { request: { url, query } } = ctx;
  let content = '';
  if (url == '/') {
    content = fs.readFileSync('./index.html', 'UTF-8');
    ctx.type = 'text/html';
  } else if (url.endsWith('.js')) {
    // 获取绝对路径
    const p = path.resolve(__dirname, url.slice(1));
    content = fs.readFileSync(p, 'UTF-8');
    ctx.type = 'application/javascript';
  } else {

  }
  ctx.body = content;
});

app.listen(3001, err => {
  if (!err) {
    console.log('server started..');
  }
});

访问http://localhost:3001就看到引入main.js的html了。现在验证之。

在src下新建log.js写一个模块服务:log

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const {log} = console;
export default log;

然后在main.js引用它:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import log from './log.js';
log('aaa');

再次访问网址,发现log被导入进来了。显然,现代浏览器显然不需要打包了。

2.2 支持导入vue框架

首先安装vue 3:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
npm i vue@next -S
2.2.1 允许/@modules/vue语法(rewriteImport)

在做vue开发时,我们通常怎样使用的脚手架?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { ref, watchEffect } from "vue"
console.log(ref)

let count = ref(0)

watchEffect(() => {
  console.log("监测到数据变化")
})

我们在main.js写入上述代码,发现报错(只支持路径模式):

因此想要支持导入vue,首先要改造import xx from 'vue'。正确应该是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import xx from '/@modules/vue';

写一个匹配xx的方法,用来重写/@modules/vue这样的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const rewriteImport = (content) => {
  return content.replace(/from ['"]([^'"]+)['"]/g, (s0, s1) => {
    // 只改写需要去node_module找的
    if (s1[0] !== '.' && s1[0] !== '/') {
      return `from '/@modules/${s1}'`
    }
    return s0;
  });
}

然后,修改js解析方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
app.use(async ctx => {
  const { request: { url, query } } = ctx;
  let content = '';
  if (url == '/') {
    // 访问index.html
    // ...
  } else if (url.endsWith('.js')) {
    // ...
    // 1. 支持import xx from '/@modules/vue';
    content = rewriteImport(content);
  } else {

  }
  ctx.body = content;
});

然后再访问,发现代码被替换为想要的样子了:

小结:接下来只要有import语法的地方都需要调用这个方法“预编译”一下。

2.2.2 从module中找vue

但是页面依然报错。因为服务器生成了一个http://localhost:3001/@modules/vue的请求。

接下来便是拦截来自/@modules/路由,去找node_modules内的依赖——找依赖一般看的就是:

1.先去node_modules文件夹下检索依赖名字2.找到对应依赖,看package.json,找到里面的module属性:

显然需要的就是这个文件。把它return 回去即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
app.use(async ctx => {
  const { request: { url, query } } = ctx;
  let content = '';
  if (url == '/') {
    // 访问index.html
    // ...
  } else if (url.endsWith('.js')) {
    // ...
    // 1. 支持import xx from '/@modules/vue';
    content = rewriteImport(content);
  } else if (url.startsWith('/@modules/')) {
    // 2. 去node_module找依赖
    // prefix 是相关依赖在node_modules下的绝对路径。
    const prefix = path.resolve(__dirname, 'node_modules', url.replace('/@modules/', ''))
    const module = require(prefix + '/package.json').module;
    const p = path.resolve(prefix, module);
    content = fs.readFileSync(p, 'UTF-8');
    ctx.type = 'application/javascript';
    content = rewriteImport(content)} else {

  }
  ctx.body = content;
});

再跑一下,发现控制台已经多了很多依赖了。

访问http://localhost:3001:

访问http://localhost:3001/@modules/vue

#### 2.2.3 屏蔽报错

各个请求是已经在network里发出去了。但是引入的vue作为前端代码依然还在报错。(报错原因:node环境全局变量process在浏览器端位undefined)。

这时可以用一个比较low的方式屏蔽错误。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
app.use(async ctx => {
  const { request: { url, query } } = ctx;
  let content = '';
  if (url == '/') {
    // 访问index.html
    content = fs.readFileSync('./index.html', 'UTF-8');
    // 3.解决报错,把node环境参数发给前端:
    content = content.replace('<script', `
      <script>
        window.process = {env:{NODE_ENV:'DEV'}}
      </script>
      <script
    `);
    ctx.type = 'text/html';
  } else if (url.endsWith('.js')) {
    // ...
    // 1. 支持import xx from '/@modules/vue';
    content = rewriteImport(content);
  } else if (url.startsWith('/@modules/')) {
    // 2. 去node_module找依赖
    // prefix 是相关依赖在node_modules下的绝对路径。
    // ..
  } else {

  }
  ctx.body = content;
});

修改main.js,写一段代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { ref, watchEffect } from "vue";

console.log(ref);

let count = ref(0);

watchEffect(() => {
  console.log("监测到数据变化", count.value);
})

setInterval(() => {
  count.value++;
}, 1000);

再次访问http://localhost:9001:

调用vue成功。

在vite中解析import语法用的是第三方模块es-module-lexer

2.3 支持.vue单文件

Main.js作为全局的入口,通常是这么写的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { createApp } from "vue";
import App from './App.vue';

createApp(App).mount('#app');

应当允许引入App.vue,期望有里面有这些内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<template>
  <div>
    <h2>count: {{ count }}</h2>
    <h2>double: {{ double }}</h2>
    <button @click="add">add</button>
  </div>
</template>
<script>
import { ref, computed } from "vue"
export default {
  setup() {
    const count = ref(1)
    const add = () => {
      count.value++
    }
    const double = computed(() => count.value * 2)
    return { count, double, add }
  },
}
</script>

解析vue文件的思路是:

•vue当中分为template和script两个标签。•把script拿出来解析js,把template拿出来解析模版。

观察vite的实现,发现vite是把style,script,template单独作为一个网络请求。借助vite的工具compiler-sfc可以实现。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
npm i @vue/compiler-sfc -S
2.3.1 解析vue中的script

在server.js中再加一个else if:当import语句以.vue结尾时:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// ...
const compilerSfc = require('@vue/compiler-sfc');
// ...

app.use(async ctx => {
  const { request: { url, query } } = ctx;
  let content = '';
  console.log(url)
  if (url == '/') {
    // 访问index.html
    // ...
  } else if (url.endsWith('.js')) {
    // 支持import xx from '/@modules/vue';
    // ...
  } else if (url.startsWith('/@modules/')) {
    //  去node_module找依赖
    // ...

  } else if (url.endsWith('.vue')) {
    // 引入vue文件
    const p = path.resolve(__dirname, url.slice(1));
    let _content = compilerSfc.parse(fs.readFileSync(p, 'UTF-8'));
    console.log(_content)

  } else { }
  ctx.body = content;
});

这时打印_content值是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  descriptor: {
    filename: 'component.vue',
    source: '<template>\n' +
      '  <div>\n' +
            // (略)...
      '</script>\n',
    template: {
      type: 'template',
      content: '\n' +
        '  <div>\n' +
        '    <h2>count: {{ count }}</h2>\n' +
        '    <h2>double: {{ double }}</h2>\n' +
        '    <button @click="add"></button>\n' +
        '  </div>\n',
      loc: [Object],
      attrs: {},
      map: [Object]
    },
    script: {
      type: 'script',
      content: '\n' +
        'import { ref, computed } from "vue"\n' +
        'export default {\n' +
        '  setup() {\n' +
        '    const count = ref(1)\n' +
        '    const add = () => {\n' +
        '      count.value++\n' +
        '    }\n' +
        '    const double = computed(() => count.value * 2)\n' +
        '    return { count, double, add }\n' +
        '  },\n' +
        '}\n',
      loc: [Object],
      attrs: {},
      map: [Object]
    },
    scriptSetup: null,
    styles: [],
    customBlocks: []
  },
  errors: []
}

发现不但有解析的内容,包括script,template的内容都打印出来了。我们需要的就是这段js。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    // 引入vue文件
    const p = path.resolve(__dirname, url.slice(1));
    let _content = compilerSfc.parse(fs.readFileSync(p, 'UTF-8'));

    ctx.type = 'application/javascript';
    content = rewriteImport(_content.descriptor.script.content)
      .replace('export default', 'const __script = ');

再刷新,看到app.vue的网络请求实际上变成了script里的那段js:

2.3.2 解析模板

App.vue还有template模板。需要把它抽离出来,解析为一个js,让它去生成html。

我们观察官方的vite项目,发现模板也是发起了一个请求,请求地址同样是App.vue,但是get参数不同(?type=template&t=时间戳)。时间戳是做缓存用的,不需要,我们保留template即可。

再然后,发现这是一个请求,所以不能用endsWith了,只能用indexOf。并且需要对type进行分类讨论。

拿到template -> 需要一个插件把template解析为render函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
npm i @vue/compiler-dom -S

届时可以调用compileDom.compile方法,抓取里面的html。

综上:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  // ...
} else if (url.indexOf('.vue') > -1) {

  // 引入vue文件,分离get请求参数
  const p = path.resolve(__dirname, url.split('?')[0].slice(1))
  let _content = compilerSfc.parse(fs.readFileSync(p, 'UTF-8'));

  if (!query.type) {
    // 处理.vue中的script
    ctx.type = 'application/javascript';
    _content = rewriteImport(_content.descriptor.script.content)
      .replace('export default', 'const __script = ');

    content = `${_content}\n` +
      `import { render as __render } from "${url}?type=template"\n` +
      `__script.render = __render\n` +
      `export default __script\n`;
  } else if (query.type == 'template') {

    // 处理.vue中的模板template
    const template = _content.descriptor.template;
    const render = compileDom.compile(template.content, { mode: 'module' }).code;
    ctx.type = 'application/javascript';
    content = rewriteImport(render);

  }

}

再运行:

可见该部分内容已经被解析为js。

这时再运行:已经看到计数器了。

2.4 支持其它模块

实际上你需要解析其它许多内容,比如style,sass,less,typescript等等。实际上就是原来webpack中各种loader的功能

此处以样式为例。

在main.js中引入样式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { createApp } from "vue";
import App from './App.vue';
import style from 'App.css';

createApp(App).mount('#app');

src/App.css代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
h2 {
  color: red;
}

思路就是在app.use中继续加else if。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 else if (url.endsWith('.css')) {
  const p = path.resolve(__dirname, url.slice(1));
  const _content = fs.readFileSync(p, 'UTF-8');
  ctx.type = 'text/css';
  content = _content;
}

再运行,你会发现,从ctx返回这段css是没卵用的。——思路还是返回一段js。通过js去创建全局的script标签。然后把样式塞进去。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
else if (url.endsWith('.css')) {
    const p = path.resolve(__dirname, url.slice(1));
    let _content = fs.readFileSync(p, 'UTF-8');

    _content =
      `const css = '${_content.replace(/\n/g, '')}';\n` +
      `let link = document.createElement('style');\n` +
      `link.setAttribute('type','text/css');\n` +
      `link.innerHTML = css;\n` +
      `document.head.appendChild(link);\n` +
      `export default css;`

    ctx.type = 'application/javascript';
    content = _content;
  }

那么样式引入功能就实现了。所以你这里import的实际是一段js

你也许会说,那么多else if已经很难看了。但在vite的真实实践中,这是通过中间件“链”起来的。中间件版可自行实现。此处不多赘述了。

References

[1] vite: https://github.com/vuejs/vite

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一Li小麦 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
腾讯和字节待遇_字节和腾讯哪个值得去
大家好,我是鱼皮,最近这段时间,应该是正值大三的小伙伴最忙碌的时候,不少同学都在准备秋季校园招聘。
全栈程序员站长
2022/11/08
2.3K0
腾讯和字节待遇_字节和腾讯哪个值得去
聊聊我在腾讯和字节工作感受
大家好,我是鱼皮,最近这段时间,应该是正值大三的小伙伴最忙碌的时候,不少同学都在准备秋季校园招聘。
程序员鱼皮
2021/08/12
3.3K0
加入字节跳动 455 天,谈一谈我的感受
老读者知道我在 2019 年与朋友合伙创业搞了一年,因为一些变故离开创业团队,于 2020 年年初加入字节跳动,迄今一共 455 天,感谢 Barry 同学的内推,感谢字节跳动的“收留”。
范蠡
2021/04/23
10.3K0
Spring Cloud Alibaba实战派作者告诉你所不知道的阿里巴巴的工作经历
说起阿里巴巴,我想大部分程序员都想去这个大厂去体验一下,毕竟大厂的工资高啊,能赚钱才是硬道理,做一个能够赚更多钱的男人,才会有更听话的女朋友,是不是这个道理呢?
35岁程序员那些事
2022/12/30
3180
在字节跳动的实习经历分享 | 万字求职指南
原来是字节跳动公司发来的周年庆祝福,看到这个短信,我真是太激动了!瞬间回想起了当年在字节跳动实习的往事,眼泪止不住地往下流啊,好想大喊一句,爷的青春回来啦!
程序员鱼皮
2021/06/13
1.8K0
鹅厂7年终有离开之日,记离职鹅厂最后30天的真实心路历程
本文来自公众号“玩不好就别玩”原创分享。本次文章内容为个人真实经历,记录了作者个人离职鹅厂前最后一个月工作交接过程中的心理变化历程。内容虽平凡无奇,但同为程序员的你我,感同身受。
JackJiang
2019/03/01
1.5K0
不同企业程序猿的一天
说起程序猿... 你还只能想到格子衫? 今天,小E就带你走进不同企业的程序猿 看看这一 物种 职业 每天究竟在做什么 以下内容均来自互联网,小E仅作整理 纯属娱乐 南山区互联网大厂程序猿的一天 8:00-9:30  通勤时间,一半的时间堵在深南大道上 9:30-10:30 如果昨天下班路上想到了某个需求的思路,就会在这个时候开始边吃早餐边敲代码了,不然就和旁边小哥扯扯NBA 10:30-12:00 开晨会,没我什么事就低头看看开发者头条、掘金等等,学习学习 12:00-14:30
腾讯NEXT学位
2020/08/13
4250
实习第一天,我就辞职了
鱼皮最新原创项目教程,欢迎学习 大家好,我是鱼皮。今天这篇文章是 编程导航星球 的拳辉同学的真实实习经历分享,希望对正在找工作的小伙伴有所帮助和启发。 星球原文链接:https://t.zsxq.com/0bH99BrO7 实习第一天 今天去了心心念念的实习,来到公司的前一天晚上,我兴奋得整晚睡不着。 刚来到公司的时候,热情的人事小姐姐帮我办理好手续,拉我进群,下载钉钉,录指纹,让我十分开心,自己终于可以挣钱了哈哈。 组长带我来到工位,眼前的画面让我有点不太适应。当组长在小组面前介绍我的时候,同事们并
程序员鱼皮
2023/03/29
5230
实习第一天,我就辞职了
程序员的真实生活写照:拿命换钱
北京的西北角是个特别的区域,这里汇集了众多互联网及IT企业,实力雄厚的上市公司将自家logo悬挂在大厦的顶端,而刚起步的创业公司也会选择在这里租下一亩三分地。
用户1257393
2018/11/22
5130
我的程序员之路02:大数据实习篇
专升本的第一学年末,凭借着自学Java拿到了人生的第一份Java开发的实习offer。我深知那时候我的Java水平有限,也深知能拿到offer并不是因为我多优秀,而是公司一批招了20多个人。
叫我阿柒啊
2021/05/17
3860
我的程序员之路02:大数据实习篇
实习一个多月,扒一扒鹅厂实习体验
我可能对三餐的要求不是很大,三年初中,三年高中,四年大学基本都是吃食堂吃外卖,这么多年的食堂外卖都吃过来了,比起这些,显然公司的伙食肯定会比较好一些,所以呢,在吃饭方面,我也没啥好嫌弃的。
帅地
2020/05/09
1.6K0
实习一个多月,扒一扒鹅厂实习体验
一个算法工程师的猝死,90后们到底是“硬刚”还是“硬抗”?
2月22日深夜,脉脉上“字节跳动同事圈”板块,一则“字节(字节跳动)又猝死一位,妻子怀孕两个月,惨...”的消息被广泛传开。据知情人士,事情发生后,字节跳动5个hr陪着猝死员工妻子,怕她有个三长两短。
数据猿
2022/04/06
3950
一个算法工程师的猝死,90后们到底是“硬刚”还是“硬抗”?
知道阿里加班,没想到加班这么恐怖......
朋友是阿里的一名程序开发,俗称敲代码的。自从进了阿里,我们就没在周六聚过。因为他那个部门基本都是996工作制。
JAVA葵花宝典
2019/05/24
2.5K0
都是实习生,但态度却截然不同
上周逛牛客的时候看到一篇帖子,楼主从自己的视角讲述了几个实习同事的经历,让我看了顿时感觉好真实,因为我们组的几个实习生就跟楼主挺像了,跟大家分享一下,以下是正文:
拓跋阿秀
2022/09/28
3100
真实经历,互联网大厂升职加薪那些事
我的第一份工作,在三星电子北京通信研究院。11年8月份,也就是研二暑假开始在这里实习,12年3月底硕士毕业,正式入职,月薪9980元。
纯洁的微笑
2018/09/30
1.3K0
真实经历,互联网大厂升职加薪那些事
美团点评实习生面试经历+转正面试
我是通过实习生转正拿到的美团点评offer,岗位为:美团外卖结算组的后端开发工程师(Java)。 实习生面试 美团点评的实习生招聘比较喜欢内推,没有网申和笔试的环节,这一点大家要注意。2016年的5月号,我听到在大众点评工作的同学说美团点评开始招聘暑期实习生了,我查看了一下美团点评各个业务线主要负责的内容,加上同学的建议,最后选择了美团外卖事业部。 一面 在5月12号中午,我接到美团一面面试官预约面试的电话,开始面试官以为我在北京,当时我刚回到青岛,于是面试官提出帮我询问能不能电话面试,5月13号面试官打来
牛客网
2018/04/28
4.1K0
虾皮真实裁员经历...
点击关注公众号,Java干货及时送达 来源:zhihu.com/question/554345647/answer/2681440160 原以为之前虾皮裁员之后会消停一波,没想到才只是拉开了序幕,之后的暴风雨更加强烈,在9月19号,虾皮又开始了新一批的裁员,其中有不少毕业的同学是还没过试用期的应届生。这篇文章是经历了这次事情的一位同学写的,正文如下。 昨天的经历很魔幻,甚至今年的经历都很魔幻。 过年疫情没回家,过了年答辩拿到硕士学位准备休息下入职,结果3月被封在家三个多月,4月被封禁在家线上入职的SHDC
Java技术栈
2022/10/08
9390
虾皮真实裁员经历...
在美团工作是种什么样的体验?
时间真是快,转眼间变成打工人也有一年的时间了,最近几天朋友圈被各个同学的答辩刷屏了。去年自己过年回到家里,再回学校就是领毕业证了,经历了可能是唯一一年的云答辩。学生时代的最后一年,对未来的工作充满了想象,一直想知道工作后会是什么样子,每天会干些什么,这里就分享一下自己一年以来在美团的工作和生活。
windliang
2022/09/23
1.4K0
在美团工作是种什么样的体验?
996扎心众生相:穷,就要拼命工作
导读:最近程序员界发生了一件大事,有人在知名代码托管平台GitHub上发起了一个名为“996.ICU”的项目,以此抵制互联网公司的996工作制,立即得到大批程序员响应。
IT阅读排行榜
2019/04/25
7320
996扎心众生相:穷,就要拼命工作
从家里到阿里,学弟求职的一年
大家好,我是鱼皮,今天给大家分享一位学弟小六的 超完整 学习和求职经验,不需要我太多的介绍,他的故事和干货都在文章里了,希望对大家有帮助。
程序员鱼皮
2021/07/24
6820
相关推荐
腾讯和字节待遇_字节和腾讯哪个值得去
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档