vue-cli

vue-cli是一种基于vue.js进行快速开发的完整系统,通过@vue/cli+ @vue/cli-service-global快速开始零配置原型开发,能够用于 快速原型设计简化应用程序搭建进行高效项目管理。它由主要的三个组件组成:

  • 通过@vue/cli搭建项目脚手架。cli是一个全局安装的npm包,提供了终端里的vue命令,可以由此创建一个项目脚手架(vue create)、构建新想法的原型(vue serve)及图形化界面管理项目(vue ui)。

  • 运行时依赖@vue/cli-service它是一个npm包,局部安装在每个@vue/cli创建的项目中,构建于webpackwebpack-dev-server之上。项目内部的vue-cli-service命令,提供servebuildinspect实现开发打包检查内部webpack配置功能。

  • 通过cli插件进行扩展。cli插件是向vue项目提供可选功能的npm包,名字以@vue/cli-plugin-(内置插件)vue-cli-plugin-(社区插件)开头,开发中通过vue add命令添加。在项目中运行vue-cli-serviec命令时,会自动解析并加载package.json中列出的所有cli插件。

开发

安装

安装命令

npm install -g @vue/cli

检查版本

vue --version //3.10.0

快速原型开发

安装全局依赖

npm install -g @vue/cli-service-global

使用vue servevue build命令对单个*.vue文件进行快速原型开发

vue serve fileName //开发环境下为js/vue文件启动一个服务器
vue build fileName //生产环境零配置构建一个js/vue文件

创建项目

运行以下命令创建一个项目,可以根据提示选取默认配置(babel+eslint)和手动选择需要的特性

vue create 项目名

配置

webpack基础相关

基本概念

webpack是一个js的静态模块打包工具。对于webpack来说,一切文件皆为模块,处理应用程序时会在内部构建一个依赖图,会从main.js出发,识别出源码中的模块化导入语句,递归地找出入口文件的所有依赖,将其打包生成一个或多个bundle
| 概念 | 用途 |
| :-------- | :--------|
| entry|webpack将这个模块作为构建内部依赖的开始|
| module| webpack中一切皆模块|
| chunk| 一个chunk由多个模块组合而成,用于代码合并与分割|
| loader|模块转换器,将非js内容按需转换。test用于标识被转换的文件,use指示文件被转换时应该使用的loader,并且在数组中从右到左地解析执行|
| plugin|解决loader无法实现的其他事情,是一个具有apply方法的js对象,apply方法会被webpack complier调用,且complier对象可在整个编译声明周期访问。由于插件可以携带参数/选项,必须在webpack配置中,向plugin传入new实例。如html-webpack-plugin为应用程序生成一个html文件,并自动注入所有生成的bundle|
| output|告诉webpack在哪里输出它所创建的bundle,以及如何命名这些文件,生成文件默认放置在./dist文件夹中|

//ConsoleLogOnBuildWebpackPlugin.js
const pluginName = 'ConsoleLogOnBuildWebpackPlugin'

class ConsoleLogOnBuildWebpackPlugin{
  apply(compiler){//compiler hook的tap方法的第一个参数,应为驼峰式命名的插件的名称,建议为此使用一个常量,以便可以在所有hook中复用
    complier.hooks.run.tap(pluginName, compilation = {
      console.log('webpack构建过程开始')
    })
  }
}

配置示例

// build/webpack.base.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
  entry: './src/js/main.js'
  module: {
    rules:[
      { test: /\.vue$/, use: 'vue-loader'}
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'})
  ],
  output: {
    path: path.resolve(__dirname,'dist'),
    filename: 'js/[name].js'
  }
}

解析原理

在使用webpack构建的应用程序中,主要有三种类型的代码:

  • 与业务有关的代码;
  • 代码依赖的第三方库;
  • webpackruntimemanifest,管理所有模块交互。

runtime,及伴随的所有manifest数据,是指在浏览器运行过程中,webpack用来连接模块化应用程序所需的所有代码,即指在模块交互时,连接模块所需的加载和解析逻辑,包括已经加载到浏览器中的连接模块逻辑和尚未加载模块的延迟加载逻辑。

compile开始执行、解析和映射应用程序时,会保留所有模块的详细要点,这个数据集合称为manifest,当完成打包并发送到浏览器时,runtime会通过manifest来解析和加载模块。无论选择哪种模块语法,importrequire语句转换为__webpack_require__方法,此方法指向模块标识符(module identifier),通过使用manifest中的数据,runtime将能够检索这些标识符,找出每个标识符背后对应的模块。

通过使用内容散列(content hash)作为bundle文件的名称,在文件内容修改时,会计算出新的hash,浏览器会使用新的名称加载文件,从而使缓存无效。这这样即使内容没有明显修改,某些hash还是会改变,因为注入的runtimemanifest在每次构建后都会发生变化。

loader和插件相关

每个cli插件会包含一个(用来创建文件的)生成器和一个(用来调整webpack核心配置和注入命令的)运行时插件。使用vue create创建新项目时,部分插件会根据预设特性被预安装好,如果想在创建好的项目中安装一个插件,使用vue add @vue/eslint,这个命令将@vue/eslint解析为完整的包名@vue/cli-plugin-eslint,然后从npm安装它,调用它的生成器。

调整webpack配置最简单的方式是vue.config.js中的configureWebpack选项提供一个对象,这个对象会被webpack-merge合并入最终的webpack配置。如果需要基于环境有条件地配置行为,或者想要直接修改配置,就换成一个函数(函数环境变量被设置之后懒执行),可以直接使用config参数来修改webpack中的配置,或者返回一个对象进行merge处理。还可以使用chainWebpack改变webpack内部配置,这种方式是链式修改,而configureWebpack倾向于整体替换和修改

//vue.config.js
module.exports = {
  configureWebpack: config => {
    if(process.env.NODE_ENV === 'production'){
      //为生产环境修改配置
    } else {
      //为开发环境修改配置
    }
  }
}

vue-cli内部的webpack配置是通过webpack-chain维护,它可以定义具名的loader规则和插件,并且可以在后期进入这些规则并对他们的选项进行修改。

loader

  • 修改loader选项。对于css相关loader来说,推荐使用css.loaderOptions而不是直接链式指定loader,是因为每种css文件类型都有多种规则,而css.loaderOprtions可以确保通过一个地方影响所有规则。
//vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
    .rule('vue')
    .use('vue-loader')
      .loader('vue-loader')
      .tap(optopns => {
        //修改它的选项
        return options
      })
  }
}

  • 添加一个新的loader
//vue.config.js
module.exports=  {
  chainWebpack: config => {
    //GraphQL Loader
    config.module
    .rule('graphql')
    .test(/\.graphql$/)
    .use('ggraphql-tag/loader')
      .loader('graphql-tag/loader')
      .end()
  }
}
  • 替换一个规则里的loader
//vue.config.js
module.exports = {
  chainWebpack: config => {
    const svgRule = config.module.rule('svg')
    svgRule.uses.clear()
    //清除所有的loader,否则接下来的loader会附加在该规则的所有loader之后
  }
  svgRule
    .use('vue-svg-loader')
      .loader(vue-svg-loader)
      //添加要替换的loader
}

插件

  • 使用插件
//vue.config.js
module.exports = {
  chainWebpack: config => {
    config.plugin('assetToJson')
    .use(AssetsWebpackPlugin, [
      {/*配置项*/}
    ])
  }
}
  • 修改插件选项
//vue.config.js
module.exports = {
  chainWebpack: config => {
    config.plugin('html')
    .tap(args => {
      return [/*传递给html-webpack-plugin构造函数的新参数*/]
    })
  }
}

可以通过vue inspect来确认变更,vue-cli-service暴露了inspect用于审查解析好的webpack配置,全局的vue可执行程序同样提供了inspect命令,会将解析出来的webpack配置、包括链式访问规则和插件的提示打印到stdout,也可以将其输出重定向到一个文件以便进行查阅

vue inspect > output.js

page相关

相较于vue cli2vue cli3搭建的项目中没有config目录和build目录,默认封装了项目运行常用的webpack常用配置,可以通过vue inspect plugins查看默认插件及用途。添加自定义配置需要在根目录中手动添加vue.config.js文件,它会被@vue/cli-service自动加载。

multi-page模式下构建应用,每个page应该有一个对应的js入口文件,它的值是一个对象,key是入口的名字,值是包括entry(必选)、templatefilenametitlechunks的字符串

//vue.config.js
module.export = {
  pages: {
  index: {
    entry: 'src/index/main.js', //入口
    template: 'public/index.html', //模板
    filename: 'index.html', //在dist/index.html的输出
    title: 'Index Page',
    chunks: ['chunk-vendors','chunk-common','index'],//页面中包含的块,通常包含提取出来的通用块和依赖块
  }
  }
}

css相关

webpack不支持原生解析css文件,如果要支持非js类型的文件,需要使用loader机制,本质上是一个模块转换器,将模块内容按需转换。

css modules

是一个用于模块化和组合css的系统,若想在js中导入css或其他预处理文件,文件应以.module.(css|less|sass|scss|styl)结尾,若想去掉文件名的.moudle,设置vue.config.jscss.modulestrue,通过<style module>以开箱即用的方式在*.vue文件中使用CSS Modules具体使用

css.loaderOptions

webpack的预处理器传递选项,支持的loadercss-loadersass-loaderless-loaderpostcss-loaderstylus-loader

css.extract

是否将组件中的css提取到一个独立的css文件中,而不是动态注入到cssinline代码中。生产环境下是true,开发环境下是false

//vue.config.js
module.exports = {
  css: {
    modules: true
    loaderOptions: {
      sass: { //这里的选项会传递给sass-loader
        data: `@import "~@varibles.css"` //@/是src/的别名,向sass样式传入共享的全局变量
      },
      postcss: { //这里的选项会传递给postcss-loader
        plugins: [ 
          require('postcss-pxtorem') //新增插件
        ]
      }
    }
  }

}

babel配置相关

babel是一个js编译器,用于将ECMAScript 2015+的版本代码转换为向后兼容的 js语法,以便能够运行在当前和旧版本的浏览器中。在.babelrcbabel.config.js文件中,主要对presetsplugins进行配置。可以在plugins中一个一个添加插件,还可以以preset的形式启用一组插件,插件在presets前运行。

presets

presetsbabel插件集合的预设,告诉babel将编译哪些类型的代码。可以接收参数,参数名由插件和参数对象组成一个数组,数组中的解析执行顺序是从后往前的。
babel-preset-env可以根据开发者的配置,按需加载插件,使得支持的目标环境的版本获得对应的js语法和浏览器功能支持,并且将语法和环境映射到对应的babel转换插件和core-js polyfills

属性名 属性值 描述
targets 字符串/字符串数组/对象 描述项目的目标环境
loose 布尔值 false生成更简单的ES5代码true尽可能遵循ES6的语义
modules 'amd'/ 'umd'/ 'systemjs'/ 'commonjs'/ 'cjs'/ 'auto'/ 'false' 启用将ES6模块语法转换为其他模块类型
include 数组 包含使用的plugin
exclude 数组 包含不使用的plugin
useBuiltIns 'usage'/'entry'/false 设置babel-preset-env如何处理polyfill,分别为根据配置的浏览器兼容和代码中用到的API进行polyfill按需添加、引入浏览器不兼容的polyfill、不对polyfill做操作

如果没有配置一些规则,babel只默认转化js句法,不转换新的API,如InteratorGeneratorSetMapProxyReflectSymbolPromise等全局对象,一些定义在全局对象上的方法(全局静态函数)(如Object.assignArray.from)以及一些实例方法Array.prototype.includes不会转码,可以使用babel-polyfill做兼容,这种做法会污染全局变量,并且把整个依赖包放进去,项目打包以后体积增大。参考

  • 使用@babel/preset-envuseBuiltInscore-js模块中直接引入参考
  • 手动导入单个polyfill
  • 使用babel-runtimebabel-plugin-transform-runtimebabel-runtime是一个以模块化方式实现函数的包,会在输出中注入一些相同的代码导致重复。babel-plugin-transform-runtime避免编译的输出中存在重复,能够为代码创建沙盒环境,防止污染全局范围。转换器将这些内置函数别名为core-js,这样可以无缝使用这些内置函数不需要再使用polyfill

plugin

插件的执行从前往后执行,可以用 插件 转换代码,模块转换插件包括,参考

  • @babel/plugin-transform-modules-amd
    AMD规范非同步加载模块,允许指定回调函数。在浏览器环境,从服务端加载模块,必须采用非同步模式,浏览器一般采用AMD规范。
  • @babel/plugin-transform-modules-commonjs
    CommonJS规范规定,每个模块内部,module变量代表当前模块,这个变量是一个对象,它的exports属性是对外的接口,加载某个模块,其实是加载该模块的module.exports属性。加载模块同步,只有加载完成,才能执行后面的操作。一旦输出一个值,内部的变化就影响不到这个值。
    Node应用由模块组成,采用CommonJS模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里定义的变量、函数、类都是私有的,对其他文件不可见。在服务器端,模块的加载是运行时同步加载,在浏览器端,模块需要提前编译打包处理。ES6模块是动态引用,并且不会缓存值,模块里的变量绑定其所在的模块,输出的是值的引用。
  • @babel/plugin-transform-modules-systemjs
  • @babel/plugin-transform-modules-umd
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270