vue-cli 将被 create-vue 替代?初始化基于 vite 的 vue3 项目为何如此简单?

大家好,我是若川。最近组织了源码共读活动《1个月,200+人,一起读了4周源码》,已经有超50+人提交了笔记,群里已经有超1500人,感兴趣的可以点此链接扫码加我微信 ruochuan12

create-vue公开了,可以使用npm init vue@next替代vue-cli快速初始化vue3项目。我粗看了源码,发现只有300行左右,打算加入到源码共读计划大家一起学习。

没想到源码共读群里的小伙伴 upupming 就很迅速的学习了源码并且输出了文章。本文是upupming投稿。

原标题:create-vue 发布并成为官方推荐,未来将替换 vue cli,看看 Vue Contributor Days 说了哪些内容(附 create-vue 源码解析)


美国时间 2021 年 10 月 7 日早晨,This Dot Media 邀请了 Vue 的核心成员和 Vue Community (例如 Quasar, Ionic 开发者等)的一些主要贡献者举办了一个 Vue Contributor Days 在线会议,长达两个半小时,会上 vue-cli 的核心贡献者胖茶也在同一天公开了全新的脚手架工具 create-vue[1],我也是看到 antfu 发推就关注了一下,看完直播回放[2]之后收获很大,这里做一些总结并且分析一下最新发布的 create-vue 的源码。

主要关注了尤大的 talk,PPT 链接[3]在这里,我转载到了我的 GitHub 上[4],大家可以下载来看一下最新的进展,另外还有胖茶现场演示了如何使用 create-vue。

1. 主要内容

Vue 3.2 在 2021.08.09 就已经发布了,最重要的就是 <script setup> 不再是实验阶段了,可以稳定使用。之前用过 composition API 的朋友都会觉得比较麻烦,<script setup> 主要是为了作为语法糖简化其写法,可以参考文档[5]和 RFC[6]。Vue 3.2 新增了一些新功能,如 defineCustomElementv-memo 等等。

尤大还提到了新的 Ref Transform 提案,虽然现在 TS 环境下多一个 .value 没有什么太大的问题,类型提示能够自动补全做的很好,但是 Ref Transform 提案可以进一步简化省去 .value 这一步,而且 TS 支持也做好了:

9b25c9e0cd13ecf688f07433a9ac7d35.png
20211009173845

使用 cout = $ref(0) 定义之后,使用 count 的时候会被编译成 count.value,同时还有一个反向操作 $$,重新把 reactive value 变成 ref。感觉这个简化确实很有前途,可能会被广泛使用。

另外 Vue 团队正在积极准备 3.3 版本,主要集中精力在优化 SSR 相关的功能。可以期待一下。

尤大给出了最新的官方推荐:

501deb370e17a3bbe496524a613b029c.png
20211009174500
  • 推荐使用 create-vue 替换 vue cli,注意如果你的项目如果使用 vue cli 创建的,能够稳定使用的话,暂时没有提供转换成 create-vue 项目的方案,而且也不建议修改大型项目的基础配置。以后的新项目大家可以使用 create-vue 来创建更加快的应用,因为 create-vue 的模板项目都是基于 vite 来进行构建的了。

  • 推荐使用 VSCode 的 Volar 插件而不是 Vetur 来获取更好的 TypeScript 支持(script setup 支持地很好, vue-tsc 表现和 volar 一致,因为都是用的一个 language service (@johnsoncodehk 开发的 vscode-vue-languageservice)),卸载 Vetur 安装 Volar 即可。

  • 状态管理的话,正在考虑在 vuex next / Pinia (也是新出来的一个状态管理工具) / vue core 三者中考虑一个新的主推

另外 Vue 3 的官方文档正在快速更新,新版的文档可以在 https://github.com/vuejs/docs/tree/next 看到源码,部署在 https://vue-docs-preview.netlify.app/ 。里面新增了许多 example,可以看 Options API 和 Composition API 两种格式,Composition API 已经都是用 script setup 来写的了,另外也有 HTML 和 SFC 两种不同的版本,感觉用 example 学起来方便很多。同时也提供了 tutorial 和 guide 两种学习方案,非常体贴。

胖茶介绍了 create-vue 的使用,令人兴奋的是,所有的模板现在的构建工具全部都是基于 vite 而不是 vue cli (Webpack) 的了,开发效率大大提升,同时使用 cypress 来作为自动测试的工具。之前 Vue 2 单元测试用的是 Jest,但是 Jest 对 Vue 3 的编译支持的不是很好,所以选择了 cypress 同时做单元测试和 E2E 测试。整个 create-vue 包的依赖数量非常少,很多没有必要的依赖都没放,而且胖茶自己做了一个预先打包导致下载速度变快了许多。同时创建的模板项目也足够轻量。

后面 antfu 还介绍了最近他开发的 unplugin[7],支持一个插件写完,Rollup、Vite、Webpack 4、Webpack 5 都能使用,这个还没有详细使用过,下次有机会细看。

2. create-vue 源码解析

首先看目录结构:

  • index.js 是整个 CLI 的打包入口,所有逻辑都是从这里开始的

  • utils 包含了用到的一些工具函数

  • template Vue 项目模板,例如默认的 default 模板、带 router 的模板、带 ts 支持的模板等等。

  • playground 利用 create-vue 生成的项目的快照结果,在运行 pnpm test 时会用到,测试生成的模板项目代码的正确性

非常简洁明了,好在项目处于刚开始的阶段,index.js 只有 300 多行,可以很容易了解其中的细节。

2.1 整体流程

  • 使用 prompt 询问用户一系列 Yes/No 的问题,看用户需要哪些 feature,包括 TS, JSX, router, vuex, cypress。同时也会询问包名和是否覆盖已经存在的文件夹(如果之前已经创建过内容的话)。

  • 验证包名是否合法,将不合法包名转换成合法的。

  • 写入带包名和版本号的 package.json

  • 调用 render 函数,首先使用 render('base') 创建一个基础的模板,接下来按照用户需要哪些 feature,往已经创建的项目中添加对应的模板,例如 render('config/jsx') 就是对基础模板添加了 JSX 支持。

  • 如果需要 TS 支持的话,后面有个特殊操作把所有 JS 重命名为 TS。将 jsconfig.json 重命名为 tsconfig.json

  • 默认是所有模板都包含测试的,如果用户不需要,最后需要删除一下

  • 判断当前使用的包管理器是 npm/yarn/pnpm,方便后续输出 xxx install 提示

  • 生成 README.md

  • 最后输出提示,提示用户生成成功并展示绿色(kolorist 这个包用来处理颜色)的提示消息,提示后续操作 cd xxx, xxx install, xxx dev

可以先运行一遍 npm init vue@next 感受一下具体的效果。

2.2 具体分析

可以看到,这里面最重要的还是 render 函数的实现,可以把一个相对目录下的文件给复制到最终生成的项目里面,同时还需要考虑文件相同的时候需要如何进行合并操作。这里主要看一下 render 函数的实现和一些我们以后可能用到的工具函数。

2.2.1 支持 feature flag

支持类似 npm init vue@next --vuex --ts 这种命令行参数,省去 prompt 提问环节直接开始生成项目。

// index.jsconst isFeatureFlagsUsed =typeof (argv.default || argv.ts || argv.jsx || argv.router || argv.vuex || argv.tests) ==='boolean'prompt(
// ...
{name: 'needsTypeScript',// 如果使用了 feature flag,直接 type 函数返回 null,就不会提问了type: () => (isFeatureFlagsUsed ? null : 'toggle'),message: 'Add TypeScript?',initial: false,active: 'Yes',inactive: 'No'
},
)
// ...

2.2.2 render 函数

// index.js// 所有模板根目录位于 template 之下
const templateRoot = path.resolve(__dirname, 'template')
// 传给一个模板名称,例如 `base`,对应于 template/base 这个模板
const render = function render(templateName) {// 拿到真正的模板路径 templateDir 之后使用 renderTemplate 将 templateDir 下的内容尝试生成到 root 中,这里 root 就是之前用户输入指定的目标路径const templateDir = path.resolve(templateRoot, templateName)renderTemplate(templateDir, root)
}

template/base 是一个最简单的所有结果都需要模板,它包括了 .vscodeindex.htmlvite.config.js 等这些基础性的东西。注意 vite 的理念和 Webpack 不一样,Webpack 和 esbuild 这些都是以 JS 为入口,但是 vite 是以 index.html 为入口的,使用的时候需要转换一下思维。这个模板的目录结构如下:

.
├── _gitignore
├── index.html
├── package.json
├── public
│   └── favicon.ico
└── vite.config.js

注意里面有个 _gitignore 文件,使用 _ 开头是个惯例,因为以 . 开头的都是配置文件,会影响一些 CLI 工具和编辑器的行为,所以为了避免影响而使用 _,真正 render 的过程中需要重命名成 . 开头

我们主要看 renderTemplate 这个函数,位于 util/renderTemplate.js 中。

函数签名注释可以看,就是一个复制过程,但是又不完全是直接的复制,需要有一些特殊操作要考虑:

// utils/renderTemplate.js/*** Renders a template folder/file to the file system,* by recursively copying all files under the `src` directory,* with the following exception:*   - `_filename` should be renamed to `.filename`*   - Fields in `package.json` should be recursively merged* @param {string} src source filename to copy* @param {string} dest destination filename of the copy operation*/
function renderTemplate(src, dest) {

如果发现传入的是 src 文件夹的话,递归调用 renderTemplate 处理文件夹下的每一个文件或者文件夹:

// utils/renderTemplate.jsconst stats = fs.statSync(src)if (stats.isDirectory()) {// if it's a directory, render its subdirectories and files recusivelyfs.mkdirSync(dest, { recursive: true })for (const file of fs.readdirSync(src)) {renderTemplate(path.resolve(src, file), path.resolve(dest, file))}return}

递归调用写好,下面就只需要考虑 src 是文件的情况了。

如果是 package.json 文件,并且目标路径已经存在,需要先 merge 两个 JSON 对象,然后将 dependencies, devDependencies, peerDependencies, optionalDependencies 这 4 个字段按照字母序从上到下排列好。

if (filename === 'package.json' && fs.existsSync(dest)) {// merge instead of overwritingconst existing = JSON.parse(fs.readFileSync(dest))const newPackage = JSON.parse(fs.readFileSync(src))const pkg = sortDependencies(deepMerge(existing, newPackage))fs.writeFileSync(dest, JSON.stringify(pkg, null, 2) + '\n')return}

如果文件以 _ 开头,需要重命名成以 . 开头:

if (filename.startsWith('_')) {// rename `_file` to `.file`dest = path.resolve(path.dirname(dest), filename.replace(/^_/, '.'))}

2.2.3 deepMergesortDependencies

这里有两个比较有用的函数,deepMerge 用来 merge 两个 object,相信这个也是面试的时候常考的一个题目,具体的思路就是如果都是对象的话就继续递归,递归到原始类型的时候就可以直接赋值来实现赋值了,而数组的话直接用解构赋值来一个浅拷贝就行了。

const isObject = (val) => val && typeof val === 'object'
const mergeArrayWithDedupe = (a, b) => Array.from(new Set([...a, ...b]))/*** Recursively merge the content of the new object to the existing one* @param {Object} target the existing object* @param {Object} obj the new object*/
function deepMerge(target, obj) {for (const key of Object.keys(obj)) {const oldVal = target[key]const newVal = obj[key]if (Array.isArray(oldVal) && Array.isArray(newVal)) {// key 字段对应的值都是 array,那么使用 destructuring 来 mergetarget[key] = mergeArrayWithDedupe(oldVal, newVal)} else if (isObject(oldVal) && isObject(newVal)) {// key 字段对应的值都是对象,那么递归调用target[key] = deepMerge(oldVal, newVal)} else {target[key] = newVal}}return target
}

sortDependencies 是将对象按照 key 进行排序,ES6 标准要求 object 对字符串类型的 key 按照插入序排列,对整数类型的 key 按照升序排列[8],因为依赖项都是 npm 包名,必然以字母开头,可以按照插入序保证其迭代的时候的顺序,从而使得解构赋值能够拿到正确的顺序。

export default function sortDependencies(packageJson) {const sorted = {}const depTypes = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']for (const depType of depTypes) {if (packageJson[depType]) {sorted[depType] = {}Object.keys(packageJson[depType]).sort().forEach((name) => {sorted[depType][name] = packageJson[depType][name]})}}return {...packageJson,...sorted}
}

2.2.4 清除旧项目 rm -rf

之前经常遇到一个问题是 fs.rmdirSync 这个函数只能删除空文件夹,非空文件夹会报错,搜索 Stack Overflow 给的最高票答案是用 rimraf[9],但是这里为了少引入包可以直接实现了递归删除文件的功能。用的是多叉树深搜中的后序遍历,因为需要先删除子文件和子文件夹,才能保证当前文件夹为空。实现如下:

// utils/directoryTraverse.jsexport function postOrderDirectoryTraverse(dir, dirCallback, fileCallback) {for (const filename of fs.readdirSync(dir)) {const fullpath = path.resolve(dir, filename)// 如果是文件夹,递归if (fs.lstatSync(fullpath).isDirectory()) {postOrderDirectoryTraverse(fullpath, dirCallback, fileCallback)// 子文件和子文件夹都处理好了再来用 dirCallback 处理这个文件夹dirCallback(fullpath)continue}// 如果是文件,直接用 fileCallback 处理fileCallback(fullpath)}
}function emptyDir(dir) {postOrderDirectoryTraverse(dir,(dir) => fs.rmdirSync(dir),(file) => fs.unlinkSync(file))
}

这个工具函数也非常有用,又省去了一个 npm install

3. 测试

写完代码需要进行测试保证正确性。package.json 中的测试脚本如下所示:

"build": "esbuild --bundle index.js --format=cjs --platform=node --outfile=outfile.cjs",
"snapshot": "node snapshot.js",
"pretest": "run-s build snapshot",
"test": "node test.js",

可以看到,首先是 pretest 运行 npm run build 进行打包,然后运行 npm run snapshot 生成 snapshot,生成快照过程就是各个 feature flag 排列组合一下,调用 create-vue 生成所有的可能的 feature flag 组合的模板结果,结果存放在 playground 文件夹下。排列组合可以使用二进制枚举实现。代码如下:

const featureFlags = ['typescript', 'jsx', 'router', 'vuex', 'with-tests']// The following code & comments are generated by GitHub CoPilot.
function fullCombination(arr) {const combinations = []// for an array of 5 elements, there are 2^5 - 1= 31 combinations// (excluding the empty combination)// equivalent to the following:// [0, 0, 0, 0, 1] ... [1, 1, 1, 1, 1]// We can represent the combinations as a binary number// where each digit represents a flag// and the number is the index of the flag// e.g.// [0, 0, 0, 0, 1] = 0b0001// [1, 1, 1, 1, 1] = 0b1111// Note we need to exclude the empty comination in our casefor (let i = 1; i < 1 << arr.length; i++) {const combination = []for (let j = 0; j < arr.length; j++) {if (i & (1 << j)) {combination.push(arr[j])}}combinations.push(combination)}return combinations
}const flagCombinations = fullCombination(featureFlags)
flagCombinations.push(['default'])for (const flags of flagCombinations) {createProjectWithFeatureFlags(flags)
}

之后再运行 test.js,就是对 playground 里面所有的项目依次运行 test:unit:ci(组件单元测试) 和 test:e2e:ci(E2E测试)了:

const playgroundDir = path.resolve(__dirname, './playground/')for (const projectName of fs.readdirSync(playgroundDir)) {if (projectName.endsWith('with-tests')) {console.log(`Running unit tests in ${projectName}`)const unitTestResult = spawnSync('pnpm', ['test:unit:ci'])console.log(`Running e2e tests in ${projectName}`)const e2eTestResult = spawnSync('pnpm', ['test:e2e:ci'])}
}

4. 总结

  • 看尤大的 talk 每次都有比较大的收获,有很多细小的问题都会解释的比较清晰,同时对 Vue 的未来规划能了解一些。

  • create-vue 代码简洁,依赖少,启动快,同时这次全面拥抱 vite 也将是非常好的,抛弃掉 Webpack 之后轻松了许多,开发体验提升了不少。create-vue 中有不少工具函数可以先记下来,下次需要用到的时候就不愁没处 copy 啦~

    • 不过 create-vue 现在还没有给模板添加 eslint 配置,后续可能会加上

参考资料

[1]

create-vue: https://github.com/vuejs/create-vue

[2]

直播回放: https://www.youtube.com/watch?v=gpTbH469Qog&ab_channel=ThisDotMedia

[3]

PPT 链接: https://docs.google.com/presentation/d/137pQTDQI8O1FHzn2AtjL5tjeTnr8wbON7IonkcNyBVs/edit#slide=id.p

[4]

我的 GitHub 上: https://github.com/upupming/frontend-learning-map/tree/main/slides/State_of_Vue_ThisDot_Meetup_Oct_2021.pdf

[5]

文档: https://v3.vuejs.org/api/sfc-script-setup.html#basic-syntax

[6]

RFC: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md

[7]

unplugin: https://github.com/unjs/unplugin

[8]

ES6 标准要求 object 对字符串类型的 key 按照插入序排列,对整数类型的 key 按照升序排列: https://stackoverflow.com/a/23202095/8242705

[9]

rimraf: https://stackoverflow.com/a/16605300/8242705

最近组建了一个江西人的前端交流群,如果你是江西人可以加我微信 ruochuan12 私信 江西 拉你进群。


推荐阅读

1个月,200+人,一起读了4周源码
我历时3年才写了10余篇源码文章,但收获了100w+阅读

老姚浅谈:怎么学JavaScript?

我在阿里招前端,该怎么帮你(可进面试群)

386942cef0e1bf12c7693b3e71f8fe29.gif

················· 若川简介 ·················

你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列
从2014年起,每年都会写一篇年度总结,已经写了7篇,点击查看年度总结。
同时,最近组织了源码共读活动

412073c56707ee5ed303fe4ee7da39a7.png

识别方二维码加我微信、拉你进源码共读

今日话题

略。欢迎分享、收藏、点赞、在看我的公众号文章~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275356.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

lynda ux_如何进入UX领域

lynda uxI often get asked “What is the right path I should take to get into UX?” and more often than not, I do not have a direct answer. I usually ask a lot of questions about their background, before assessing their current skills with the things they …

可以测试体育跑步的软件,某高校现跑步打卡神器 能检测出是在走还是跑

[摘要]近日&#xff0c;一批高大上的“阳光跑步神器”在东莞一所高校火了&#xff01;之所以称之“神器”&#xff0c;是由于这批机器能检测到你在走路还是在跑步&#xff0c;如果走路数据将中断。消息一出&#xff0c;学生们有赞成&#xff0c;也有大呼“吃不消”。东莞某高校…

一道很熟悉的前端面试题,你怎么答?

大家好&#xff0c;我是若川。最近这几年&#xff0c;云计算的普及和 HTML5 技术的快速发展&#xff0c;越来越多的应用转向了浏览器 / 服务器&#xff08;B/S&#xff09;架构&#xff0c;这种改变让浏览器的重要性与日俱增&#xff0c;视频、音频、游戏几大核心场景也都在逐渐…

:寻找指定和的整数对_寻找时间:如何增加设计的时间

:寻找指定和的整数对Good design derives from good thinking. And good thinking is highly correlated to how much time you spend. In every place I’ve been though, every designer seems to be thirsty for more time to design. Why does this happen, to a point whe…

linkedin爬虫_重新设计Linkedin的指导功能-用户体验案例研究

linkedin爬虫为什么选择导师 Linkedin平台&#xff1f; (Why mentorship Linkedin platform?) As a recent graduate, I went on Linkedin to seek career advice and mentorship. This idea came so naturally that I was quite surprised by the absence of a complete fea…

POJ 1797 Heavy Transportation 解题报告

分类&#xff1a;图论&#xff0c;生成树&#xff0c;最短路&#xff0c;并查集作者&#xff1a;ACShiryu时间&#xff1a;2011-7-28地址&#xff1a;ACShiryus BlogHeavy TransportationTime Limit: 3000MSMemory Limit: 30000KTotal Submissions: 11929Accepted: 3171Descrip…

曾以为只能拿8K,22届学弟字节校招心路历程

大家好&#xff0c;我是若川。最近组织了源码共读活动《1个月&#xff0c;200人&#xff0c;一起读了4周源码》&#xff0c;已经有超50人提交了笔记&#xff0c;群里已经有超1500人&#xff0c;感兴趣的可以点此链接扫码加我微信 ruochuan12这篇文章记录了江西师大学弟进入字节…

王者荣耀cpu测试软件,你的手机能否玩王者荣耀,主流处理器新版王者荣耀测试...

说道国民级手游&#xff0c;目前来看那绝对是王者荣耀和刺激战场&#xff0c;之前的话那可是王者荣耀的天下&#xff0c;甚至许多手机厂商在发布新手机的时候会专门公布王者荣耀的帧率&#xff0c;可见王者荣耀带来的影响有多大。新版王者荣耀随着王者荣耀的优化和手机系统、硬…

关于MFC遇到的一系列类型转换问题

1.LPTSTR 转换成 CString&#xff1a; (1)直接赋值 CString strText; LPTSTR lpszText _T("LPTSTR >> CString"); strText lpszText; ::MessageBox( NULL, strText , _T("标题"), MB_ICONASTERISK|MB_TASKMODAL|MB_OK );(2)CString::Format()格式化…

大萧条时期什么行业走俏_大流行时期的用户体验

大萧条时期什么行业走俏You’ve read a lot about uncertain times and social distancing. We’re all surrounded by the same words, but what exactly do they mean for the UX people? The nearest future is just the tip of the iceberg. The COVID-19 pandemic is lik…

面试官问:来实现一个Promise

大家好&#xff0c;我是若川。最近组织了源码共读活动《1个月&#xff0c;200人&#xff0c;一起读了4周源码》&#xff0c;已经有超50人提交了笔记&#xff0c;群里已经有超1500人&#xff0c;感兴趣的可以点此链接扫码加我微信 ruochuan12 参与&#xff0c;一起学习&#xff…

奇迹暖暖服务器不稳定,闪耀暖暖用土豆当服务器?开服仅半小时就崩溃,无数玩家疯狂吐槽...

大家好&#xff0c;这里是正惊游戏&#xff0c;我是你们的正惊小弟。继奇迹暖暖之后&#xff0c;叠纸游戏的3D换装类游戏《闪耀暖暖》于昨天正式开启了全平台公测。就在大家想要上游戏给女儿买好看的衣服时&#xff0c;发现游戏的服务器崩了&#xff0c;谁都登录不上去&#xf…

nda协议_如何将NDA项目添加到您的投资组合

nda协议Being on the job hunt meant I needed to update my portfolio again. I had a new project to add, but it was under an NDA and I couldn’t say too much about it. Since I’ve never had to figure out how to display an NDA project on my portfolio before, I…

程序员一定会有35岁危机吗?

大家好&#xff0c;我是若川。最近组织了源码共读活动《1个月&#xff0c;200人&#xff0c;一起读了4周源码》&#xff0c;已经有超50人提交了笔记&#xff0c;群里已经有超1500人&#xff0c;感兴趣的可以点此链接扫码加我微信 ruochuan12你好&#xff0c;我是黄老师。最近经…

hdu 2141 Can you find it? hdu1597 find the nth digit

hdu2141 唉&#xff0c;是我 想多了&#xff0c;用普通方法拼命剪枝&#xff0c;还是TLE 直接将前俩个数组的和求出来并保存&#xff0c;之后就是一个二分查找的过程了 二分的俩种写法 第一种 #include<iostream>#include<algorithm>#include<string>using …

网页开发环境的重要性_少即是多:极简方法在网页设计中的重要性

网页开发环境的重要性Written by Alan Smith由艾伦史密斯 ( Alan Smith)撰写 Minimalism has been an increasingly popular trend in the web design world. Designers may be tempted by bolder, feature-rich design because it might seem like the best way to engage us…

聊聊前端八股文?

大家好&#xff0c;我是若川&#xff0c;点此加我微信进源码群&#xff0c;一起学习源码。同时可以进群免费看Vue专场直播&#xff0c;有尤雨溪分享「Vue3 生态现状以及展望」前些天&#xff0c;我看到《剑指前端offer》一系列文章&#xff0c;被前言部分图示和文章内容惊艳到。…

服务器内存型号与频率,一张图看懂如何选择DDR4内存的频率和容量

Intel发布了代号为Skylake的第六代酷睿处理器&#xff0c;与此同时各大主板厂商也迅速推出基于100系列芯片组的各型号主板以迎接Skylake处理器&#xff0c;分别有Z170、H170及B150三个不同级别的芯片组。那针对着不同芯片组主板&#xff0c;如何选择DDR4内存的频率和容量&#…

Promise 到底是什么?看这个小故事

大家好&#xff0c;我是若川&#xff0c;点此加我微信进源码群&#xff0c;一起学习源码。还可以进《剑指前端offer》交流群。另外&#xff0c;可以进群免费看下周六Vue专场直播&#xff0c;有尤雨溪分享「Vue3 生态现状以及展望」如果你还是一个 JavaScript 初学者&#xff0c…

Vue 团队公开快如闪电的全新脚手架工具,未来将替代 Vue-CLI,才300余行代码,学它!...

1. 前言大家好&#xff0c;我是若川。欢迎关注我的公众号若川视野源码共读活动ruochuan12想学源码&#xff0c;极力推荐之前我写的《学习源码整体架构系列》jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4、koa-compose、vue-next-release…