vue cli 从入门到精通
cli 相关概念及知识
cli 是一种通过命令行来交互的工具应用,全称时 Command Line Interface。比较常⻅的就是 create- react-app,vue-cli 等,他们都能够将一段 js 脚本,通过封装为可执行代码的形式,进行一些操作。
使用 cli 之后呢,能快速的创建一些我们业务中的样板文件,比如快速创建一个项目内容,配置公共的 eslint、webpack 等等配置工具。
在封装这些内容之前,我们需要使用如下的几个库:
- commander: 命令行中的参数获取
- inquirer: 命令行的表单 (创建时的交互命令表单)
- chalk: 命令行中的可变颜色效果
- clui: 命令行中的loading 效果
- child_process:node 原生模块,提供一些方法让我们能够执行新的命令
安装
npm install -g @vue/cli
安装之后就有 vue 命令
快速创建项目的脚手架
vue create my-project
手写一个cli
要求:
- 可执行脚本 有 vue 命令
- 命令必须有一些可执行的配置 如 -V, --version; --help ( 为必填项 [options]为可选项)
- 有交互表单 --version --eslint --webpack
- 基于用户的选项定制化用户的配置
nodejs 基于 Commonjs 模块化规范 --> 都会有package.json 描述模块具体的作用
- 可执行脚本 创建 package.js
npm init --yes
入口文件 index.js 头部标记:使用 node 命令解析
#!/usr/bin/env node
package.js 中使用 bin 字段代表可执行的二进制文件,映射命令和文件
"description": "",
"bin": {
"cli": "./index.js"
},
使用 npm link 将 node 中的cli 命令 如软链指向 index.js 文件
硬链接和软链接 软链接:linux 系统中,指针指向可执行的脚本 硬链接指脚本文件已经存在在path 中
建立一个文件link1 使用 ln link1 link2 创建出完全一样的link 2 ls -al 显示都一样,独立的两个文件
-rw-r--r-- 2 zhouwenxi staff 8 2 5 11:23 link1
-rw-r--r-- 2 zhouwenxi staff 8 2 5 11:23 link2
ln -s (soft link) link3
-rw-r--r-- 2 zhouwenxi staff 8 2 5 11:23 link1
-rw-r--r-- 2 zhouwenxi staff 8 2 5 11:23 link2
lrwxr-xr-x 1 zhouwenxi staff 5 2 5 11:25 link3 -> link3
- 格式化用户输入
// index.js
#!/usr/bin/env node
console.log(process.argv)
➜ cli cli
[ '/usr/local/bin/node', '/usr/local/bin/cli' ]
process 为 node 中的全局对象,对象下有一些原生处理,如 argv,打印node 和 当前命令
➜ cli cli --flag arg2 arg3
[
'/usr/local/bin/node',
'/usr/local/bin/cli',
'--flag',
'arg2',
'arg3'
]
commander 库 基于 process.argv 配置进行一定的格式化
安装 commander inquirer 库
引入 commander (推荐commonjs 规范) 使用 program 方法,通过arguments 参数,使用 parse 解析
// index.js
const { program } = require('commander')
program.parse(process.argv)
模仿vue cli 命令,用户选择一个目录以及选择什么样的技术栈
// index.js
program
.argument('<dir>') // 必填项
.description('this is a directory!')
.action((dir) => {
console.log('--dir', dir)
})
- 交互表单
引入 inquirer
// index.js
const inquirer = require('inquirer')
// console.log(process.argv)
program
.arguments('<dir>')
.description('this is a directory!')
.action((dir) => {
// console.log('--dir', dir)
return inquirer.prompt([
{
type: 'list',
name: 'framework',
message: 'which framework do you like?',
choices: [
'vue',
'react'
]
}
]).then((answers) => {
console.log('result', dir, answers)
})
})
program.parse(process.argv)
- 基于用户选择做定制化操作
创建的项目出现在文件夹中
4.1 通过 fs (filesystem)公里文件系统
.then((answer) => {
console.log('result', dir, answers)
fs.writeFileSync('')
}) // 写入文件内容
但是上述方法创建出的内容较复杂,也不利于后期更新
4.2 通过 git 下载到某目录
.then((answer) => {
console.log('result', dir, answers)
// git clone xxx dir
})
直接更改 github 上面的内容即可,用户只需要 执行 git clone 进行下载,这里直接定义 command
通过 path 模块中的resolve 拼接路径,构造一个绝对路径
process.cwd() 执行当前脚本的路径
childProcess 中的方法childProcess.execSync执行 command
// 引入 path childProcess
const path = require('path')
const childProcess = require('childProcess')
// program
.then((answer) => {
const fullDir = path.resolve(process.cwd(), dir)
console.log('----fullDir', fullDir)
const command = `git clone https://github.com/loatheb/${answers.framework}-boilerplate.git ${fullDir}`
console.log('----command', command)
childProcess.execSync(command)
}
发布模块
npm publish
显示 全局安装模块
npm -g list --depth=0
@vue/cli 相关使用
Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供:
- 通过 @vue/cli 搭建交互式的项目脚手架。
- 通过 @vue/cli + @vue/cli-service-global 快速开始零配置原型开发。
- 一个运行时依赖 (@vue/cli-service) 一个丰富的官方插件集合,集成了前端生态中最好的工具。
- 一套完全图形化的创建和管理 Vue.js 项目的用户界面。
我们能够通过 npm install -g @vue/cli 来进行安装,安装成功之后,我们就能使用 vue 这个命 令。
npm install -g @vue/cli-service-global
启动一个服务器
vue serve xxx.vue
将目标文件构建成一个生产环境的包
vue build xxx.vue
创建一个 vue-cli-service 提供支持的新项目
vue create mytest
通过 vue ui 创建一个可视化工作区,管理项目
插件及预设
默认配置 vue-cli-service 选装babel 和 eslint 会内置加入这两项,不需要显式插入
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"vue-template-compiler": "^2.6.11"
},
全局命令会在 .bin 文件夹中,如果是局部安装的命令,则在node_modules 的 .bin 文件夹中
CLI 服务
在一个 Vue CLI 项目中,@vue/cli-service 安装了一个名为 vue-cli-service 的命令。你可以在 npm scripts 中以 vue-cli-service 、或者从终端中以 ./node_modules/.bin/vue-cli-service 访问 这个命令。
{
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
}
}
npm run serve
开发浏览器兼容性
babel 主要处理语法相关内容,并不会处理 polyfill运行时的方法,需要手动加载 polyfill
通过@vue/babel-preset-app,它通过 @babel/preset-env 和 browserslist 配置来决定项目需要的 polyfill。
默认情况下,它会把 useBuiltIns: 'usage' 传递给 @babel/preset-env ,这样它会根据源代码中 出现的 语言特性自动检测需要的 polyfill。这确保了最终包里 polyfill 数量的最小化。然而,这也意味着 如果其 中一个依赖需要特殊的 polyfill,默认情况下 Babel 无法将其检测出来。 如果有依赖需要 polyfill,你有几种选择: 如果该依赖基于一个目标环境不支持的 ES 版本撰写: 将其添加 到 vue.config.js 中的 transpileDependencies 选项。这会为该依赖同时开启语法语法转换和根据使用情况检测 polyfill。 如果该依赖交付了 ES5 代码并显式地列出了需要的 polyfill: 你可以使用 @vue/babel-preset-app 的 polyfills 选项预包含所需要的 polyfill。注意 es6.promise 将被默认包含,因为现在的库依赖 Promise 是非常普遍的。
// babel.config.js
module.exports = {
presets: [
[
'@vue/app',
{
polyfills: [
'es6.promise',
'es6.symbol'
] }
] ]
}
如果该依赖交付 ES5 代码,但使用了 ES6+ 特性且没有显式地列出需要的 polyfill (例如 Vuetify): 请使用 useBuiltIns: 'entry' 然后在入口文件添加 import '@babel/polyfill'。这会根据 browserslist 目 标导入所 有 polyfill,这样你就不用再担心依赖的 polyfill 问题了,但是因为包含了一些没有用到的 polyfill 所以 最终的包大小可能会增加。
现代模式
vue-cli-service build --modern
Vue CLI 会产生两个应用的版本: 一个现代版的包,面向支持 ES modules 的现代浏览器 另一个旧版的包,面向不支持的旧浏览器
如果支持<type="module">加载,如果不支持,根据nomodule加载
HTML Preload
preload 是一个 HTML5 的新特性,是一个新的标签,对于浏览器加载来说,对于主资源HTML和CSS的 优先级最高,其他资源优先级不统一。我们使用 preload 属性,可以让支持的浏览器提前加载资源,但 加载时并不执行,等待需要时才进行执行。 这样做的好处就是我们可以将加载和执行分离开,同时也可以控制提前加载一些大型文件,防止使用时 获取的⻚面闪烁。 我们就可以用来指定⻚面加载后很快会被用到的资源,所以在⻚面加载的过程中,我们希望在浏览器开 始主体渲染之前尽早 preload。 默认情况下,一个 Vue CLI 应用会为所有初始化渲染需要的文件自动生成 preload 提示。 这些提示会被 @vue/preload-webpack-plugin 注入,并且可以通过 chainWebpack 的
config.plugin('preload') 进行修改和删除。
Prefetch
一般用在路由中,app.js 发请求请求chunk.js,请求之后通过 jsonp 获得预获取。
是一种 resource hint,用来告诉浏览器在⻚面加载完成后,利用空闲时间提前获取用户未来可能会访 问的内容。 默认情况下,一个 Vue CLI 应用会为所有作为 async chunk 生成的 JavaScript 文件 (通过动态 import() 按需 code splitting 的产物) 自动生成 prefetch 提示。 这些提示会被 @vue/preload-webpack-plugin 注入,并且可以通过 chainWebpack 的 config.plugin('prefetch') 进行修改和删除。 举个例子:
// vue.config.js
module.exports = {
chainWebpack: config => {
// 移除 prefetch 插件 config.plugins.delete('prefetch')
// 或者
// 修改它的选项:
config.plugin('prefetch').tap(options => {
options[0].fileBlacklist = options[0].fileBlacklist || []
options[0].fileBlacklist.push(/myasyncRoute(.)+?.js$/)
return options
}) }
}
当 prefetch 插件被禁用时,你可以通过 webpack 的内联注释手动选定要提前获取的代码区块: webpack 的运行时会在父级区块被加载之后注入 prefetch 链接。 import(/* webpackPrefetch: true */ './someAsyncComponent.vue')
处理静态资源
静态资源可以通过两种方式进行处理:
- 在 JavaScript 被导入或在 template/CSS 中通过相对路径被引用。这类引用会被 webpack 处理。 当你在 JavaScript、CSS 或 .vue 文件中使用相对路径 (必须以 . 开头) 引用一个静态资源时,该资源将会 被包含进入 webpack 的依赖图中。在其内部,我们通过 file-loader 用版本哈希值和正确 的公共基础 路径来决定最终的文件路径,再用 url-loader 将小于 4kb 的资源内联,以减少 HTTP 请求的数量。
- 放置在 public 目录下或通过绝对路径被引用。这类资源将会直接被拷⻉,而不会经过 webpack 的 处理。
CSS相关
Vue CLI 项目天生支持 PostCSS、CSS Modules 和包含 Sass、Less、Stylus 在内的预处理器。 所有编译后的 CSS 都会通过 css-loader 来解析其中的 url() 引用,并将这些引用作为模块请求来 处 理。这意味着你可以根据本地的文件结构用相对路径来引用静态资源。 你可以在创建项目的时候选择预处理器 (Sass/Less/Stylus)。 如果当时没有选好,内置的 webpack 仍然会被预配置为可以完成所有的处理。
webpack 相关
调整 webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象,该 对象将会被 webpack-merge 合并入最终的 webpack 配置。
// vue.config.js
module.exports = {
configureWebpack: {
plugins: [
new MyAwesomeWebpackPlugin()
] }}
@vue/cli 源码解析
//vue-cli/packages/@vue/cli/package.json
- @vue/cli 生成的 vue 命令
vue.js
- @vue/cli vue 命令下的 create 方法
- @vue/cli-service 的 serve 命令 通过webpack 暴露的函数启动
- @vue/cli-service 的 build 命令
- @vue/cli-plugin-eslint 的 eslint 插件
- @vue/cli add 命令增加一个插件