vue-cli 3.0新特性解读

6,403 阅读12分钟

概述

在9 月 30 日的Vue.js 伦敦大会上, 作者尤雨溪介绍了 Vue 下一个版本将要发布的内容,以及 Vue 3.0 的开发路线,和后面版本的发展情况。虽然,Vue 3.0版本的正式版还没有发布,不过作为vue 项目快速构建工具的vue-cli 早已发布,我们可以通过vue-cli来了解vue 3.0的一些情况。

作为Vue的主要版本,Vue 3.0带来了诸多的重大变更,不过,开发组也非常重视兼容性问题:除了渲染函数 API 和作用域插槽语法之外的所有内容都将保持不变,或者通过兼容性构建让其与 2.x 保持兼容。总的来说,Vue 3.0 虽然会对顶级 API 进行重大的修整,但依然会保持与 2.x 的兼容。此外,2.x 的最后一个次要版本将成为 LTS,并在 3.0 发布后继续享受 18 个月的 bug 和安全修复更新。

欲了解更多的详情,可以参考下面的链接:Vue 3.0版本发布计划

重构

代码结构

为了实现更清晰、更易维护的源代码架构,尤雨溪表示将从头开始重写 3.0,并将一些内部功能分解为单独的包,以便隔离复杂性。如下图是vue 3.0的源码目录结构图。

在这里插入图片描述
前文说过,Vue团队打算在从零开始编写 3.0 版本,为的是“达到更加清晰和更易维护的架构,特别是为了让代码的贡献变得容易”。为了降低复杂性,对复杂性进行隔离,开发团队将一些内部功能拆分为了多个单独的包。例如,observer 模块将成为一个单独的包,拥有自己对外的 API 和自己的测试用例。

另外,代码库现在改为用 TypeScript 编写,虽然这会使得“熟练TypeScript”成为对新代码库进行贡献的一个前置要求,不过我们相信有类型信息配合 IDE 的支持,对于一个新的贡献者来说,要做出有意义的贡献,实际上反而会更加容易。

除此之外,Vue还对改进编译器、支持 IE 11、其他运行时改进和改进观察机制等方面内容。

监测机制

3.0 将带来一个基于 Proxy 的 observer 实现,它可以提供覆盖语言 (JavaScript——译注) 全范围的响应式能力,消除了当前 Vue 2 系列中基于 Object.defineProperty 所存在的一些局限,这些局限包括:

  • 对属性的添加、删除动作的监测;
  • 对数组基于下标的修改、对于 .length 修改的监测;
  • 对 Map、Set、WeakMap 和 WeakSet 的支持;

另外,新的 observer还提供了如下的一些特性:

  • 公开的用于创建 observable 的 API:这为小型到中型的应用提供了一种轻量级的、极其简单的跨组件状态管理解决方案。
  • 默认为惰性监测(Lazy Observation)。在 2.x版本中,任何响应式数据,不管它的大小如何都会在启动的时候监测功能。如果数据量很大的话,在应用启动的时候就可能造成严重的性能消耗。而在3.x 版本中,只有应用的初始可见部分所用到的数据会被监测,更不用说这种监测方案本身其实也是更加快的。

更精准的变动通知:举个例子:在 2.x 系列中,通过 Vue.set 强制添加一个新的属性,将导致所有依赖于这个对象的 watch 函数都会被执行一次;而在 3.x 中,只有依赖于这个具体属性的 watch 函数会被通知到。

不可变监测对象:我们可以创建一个对象的“不可变”版本,以此来阻止对他的修改——包括他的嵌套属性,除非系统内部临时解除了这个限制。这种机制可以用来冻结传递到组件属性上的对象和处在 mutation 范围外的 Vuex 状态树。

  • 更好的调试能力:通过使用新增的 renderTracked 和 renderTriggered钩子,我们可以精确地追踪到一个组件发生重渲染的触发时机和完成时机及其原因。
    在这里插入图片描述

编译器

Vue 3.0与编译器相关的代码编译将会有一个大的提升,用一句话概括为:“摇树友好”的输出;更多的 AOT 优化;更良好的解析错误;支持 source map。

  • 如果采用的是支持“摇树优化”的打包器,模板中使用到的那些可选特性,在生成的代码中将通过 ES的模块语法导入;而在打包后的文件中,那些没用到的可选特性就会被“摇掉”。
  • 由于新的虚拟 DOM 实现所带来的提升,我们可以执行一些更加高效的编译耗时优化,如静态树提升(static tree hoisting)、静态属性提升(static props hoisting);以及为运行时提供一些来自编译器的提示,以此避开子组件的规范过程 (children normalization);提供VNode 快速创建路径等等。
  • 解析器重写,以便在对模板进行编译发生错误时,可以提供错误发生的位置信息;除此之外还可以带来对模板的 source map支持;还可以支持第三方工具如 eslint-plugin-vue 和 IDE 的语言服务 (language services) 特性。

IE 11兼容

新的代码库目前只针对主流浏览器,而且我们假定他们都支持 ES2015。但是,哎,我们也知道在可预见的未来还有很多用户仍然需要支持 IE11。除了 Proxy 外,大多数 ES2015 的特性都可以用转译或者垫片的方式在 IE11 中使用。我们的计划是另外单独实现一个具有同样 API 的替代性 observer,不过是基于老式的 Object.defineProperty API;然后再通过单独构建一个使用这个实现的 Vue 3.x 版本 (build) 进行发布,不过这个单独的版本还是会有 Vue 2.x 在变动探测方面所存在的问题,所以它其实并不是一个完全兼容 3.x 的一个版本。我们也意识到这会给第三方库的作者们带来某些不便,因为他们需要考虑两个不同版本之间的兼容性问题,不过当我们真的推进到那个阶段时,那时我们肯定会确保提供一份清晰的指导。

当然,Vue 3.0还处于开发阶段,最早发布也会等到2019年了,让我们拭目以待吧。

vue-cli 3.0

vue-cli 是 vue 官方团队推出的一款快速开发 vue 项目的构建工具,具有开箱即用并且提供简洁的自定义配置等功能。 vue-cli 从 2.0 到 3.0 修改了众多的东西,下面就让我们来了解下。

创建项目

从vue-cli 3.0开始,vue的安装命令从vue-cli 改成了 @vue/cli。例如:

npm install -g @vue/cli

使用vue-cli 3.0创建项目的命令如下:

vue create my-project

除了命令创建外,3.0还增加了图形化界面创建以及管理vue项目的功能, 在创建新项目时还可以混合选用多种集成。当我们使用vue ui命令后即可使用图形化的方式来操作vue项目的源码。

在这里插入图片描述

需要说明的是,在初始化项目时系统会默认生成package.json和package-lock.json两个配置文件,它们的区别在于package.json只能锁定大版本号,而package-lock.json则能锁定安装时包的版本号,以保证多人开发时项目版本号的一致。同时,Vue在3.0版本删除了static目录,并新增了public目录,该目录主要用于存放不被webpack处理的文件和资源。

当我们使用create命令创建项目时,系统会要求我们选择一些东西。例如:

Vue CLI v3.0.3
? Please pick a preset:
  default (babel, eslint)
> Manually select features

Vue 3.0 版本支持预设配置 和 用户自定义配置,其中自定义功能配置包括以下功能:

  • TypeScript
  • Progressive Web App (PWA) Support
  • Router
  • Vuex
  • CSS Pre-processors
  • Linter / Formatter
  • Unit Testing
  • E2E Testing

可以发现,3.0 版本新加入了 TypeScript 以及 PWA 的支持。

1,在选择 CSS 预处理器后会提示选择哪一种预处理器?

  • Scss/Sass
  • Less
  • Stylus

2,eslint 规范的选择

  • ESLint with error prevention only
  • ESLint + Airbnb config
  • ESLint + Standard config
  • ESLint + Prettier

3,选择 Babel,PostCSS,ESLint 等自定义配置的存放位置

  • In dedicated config files
  • In package.json

目录结构

在这里插入图片描述

相比于Vue 2.0版本来说,Vue 3.0的目录结构则简洁很多,下面是Vue项目文件的具体含义及其作用说明。

  • node_modules:项目依赖的第三方模块;
  • public:移除static目录,新增public目录,并且 index.html 移动到 public 中,该目录主要用于存放如图片、字体等静态资源和打包后的文件;
  • src:源码存放目录,主要可以存放的有assets资源文件和源代码;
  • babelrc:将es6代码转换成浏览器能够识别的代码;
  • editorconfig:定义项目编码规范,优先级高于编译器设置的优先级;
  • index.html:项目入口文件,可以配置meta 信息或统计代码等;
  • package.json:项目配置文件,该文件主要定义了项目所需要的各种依赖模块和项目的一些配置信息;
  • package-lock.json:锁定安装时包的版本号,多人协作开发会用到;
  • webpack.config.js:webpack模块化打包的一些配置;

自定义配置

从 3.0 版本开始,系统会在项目的根目录生成一个 vue.config.js 文件,可以在此文件中添加自定义的一些配置。下面是一些常用的自定义配置:

module.exports = {
  baseUrl: '/',
  outputDir: 'dist',
  lintOnSave: true,
  compiler: false,
  // 调整内部的 webpack 配置。
  // 查阅 https://github.com/vuejs/vue-doc-zh-cn/vue-cli/webpack.md
  chainWebpack: () => {},
  configureWebpack: () => {},
  // 配置 webpack-dev-server 行为。
  devServer: {
    open: process.platform === 'darwin',
    host: '0.0.0.0',
    port: 8080,
    https: false,
    hotOnly: false,
    // 查阅 https://github.com/vuejs/vue-doc-zh-cn/vue-cli/cli-service.md#配置代理
    proxy: null, // string | Object
    before: app => {}
  }
  ....
}

调整 webpack 配置最简单的方式就是在vue.config.js中的configureWebpack选项提供一个对象,该对象将会被webpack-merge合并入最终的 webpack 配置。例如:

module.exports = {
  configureWebpack: {
    plugins: [
      new MyAwesomeWebpackPlugin()
    ]
  }
}

如果想要修改插件选项的参数,读者可以阅读webpack-chain 的 API 获取更多相关的一些源码。例如:

// vue.config.js
module.exports = {
  chainWebpack: config => {
    config
      .plugin('html')
      .tap(args => {
        return [/* new args to pass to html-webpack-plugin's constructor */]
      })
  }
}

需要说明的是,当我们更改一个 webpack 配置的时候,可以通过vue inspect > output.js输出完整的配置清单,注意它输出的并不是一个有效的 webpack 配置文件,而是一个用于审查的被序列化的格式。

ESLint、Babel、browserslist

  • Babel 可以通过.babelrc 或 package.json 中的 babel 字段进行配置。
  • ESLint 可以通过.eslintrc 或 package.json 中的 eslintConfig 字段进行配置。
  • package.json 中的 browserslist 字段指定了该项目的目标浏览器支持范围。

browserslist

我们可以在package.json配置文件中看到browserslist字段。例如:

> 1%
last 2 versions
not ie <= 8

使用 npx browserslist 可以查看项目的浏览器兼容情况,vue的支持情况如下表:

在这里插入图片描述

public目录

vue 约定public/index.html作为入口模板会通过html-webpack-plugin插件处理。在构建过程中,资源链接将会自动注入其中。除此之外,vue-cli 也自动注入资源提示(preload/prefetch),在启用 PWA 插件时注入manifest/icon/链接,并引入(inlines) webpack runtime / chunk manifest清单已获得最佳性能。

在 JavaScript 或者 SCSS 中通过 相对路径 引用的资源会经过 webpack 处理,放置在 public 文件夹的资源可以通过绝对路径引用,这些资源将会被复制,而不经过 webpack 处理。

并且,图片最好使用相对路径经过 webpack 处理,这样可以避免很多因为修改网站根目录导致的图片404问题。

新增功能

除此之外,Vue-cli还带来了两个比较有诱惑力的功能:对TypeScript和PWA的支持;

TypeScript 支持

从 3.0 版本开始中,系统选择启用 TypeScript 语法,从而大大的简化了代码,不过也带来了一些书写上的约束。例如:

import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator'
const s = Symbol('baz')
@Component
export class MyComponent extends Vue {
 @Emit()
 addToCount(n: number){ this.count += n }
 @Emit('reset')
 resetCount(){ this.count = 0 }
 @Inject() foo: string
 @Inject('bar') bar: string
 @Inject(s) baz: string
 @Model('change') checked: boolean
 @Prop()
 propA: number
 @Prop({ default: 'default value' })
 propB: string
 @Prop([String, Boolean])
 propC: string | boolean
 @Provide() foo = 'foo'
 @Provide('bar') baz = 'bar'
 @Watch('child')
 onChildChanged(val: string, oldVal: string) { }
 @Watch('person', { immediate: true, deep: true })
 onPersonChanged(val: Person, oldVal: Person) { }
}

上面的代码等价于下面的代码:

const s = Symbol('baz')

export const MyComponent = Vue.extend({
  name: 'MyComponent',
  inject: {
    foo: 'foo',
    bar: 'bar',
    [s]: s
  },
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean,
    propA: Number,
    propB: {
      type: String,
      default: 'default value'
    },
    propC: [String, Boolean],
  },
  data () {
    return {
      foo: 'foo',
      baz: 'bar'
    }
  },
  provide () {
    return {
      foo: this.foo,
      bar: this.baz
    }
  },
  methods: {
    addToCount(n){
      this.count += n
      this.$emit("add-to-count", n)
    },
    resetCount(){
      this.count = 0
      this.$emit("reset")
    },
    onChildChanged(val, oldVal) { },
    onPersonChanged(val, oldVal) { }
  },
  watch: {
    'child': {
      handler: 'onChildChanged',
      immediate: false,
      deep: false
    },
    'person': {
      handler: 'onPersonChanged',
      immediate: true,
      deep: true
    }
  }
})

可以发现,使用TypeScript语法后,代码大为精简。

PWA 支持

当我们选择启用 PWA 功能时,在打包生成的代码时会默认生成 service-worker.js 和 manifest.json 相关文件。熟悉PWA的同学都知道service-worker.js 和 manifest.json 是PWA的重要配置文件。如果读者还不了解 PWA,点击 PWA官方文档 查看。

默认情况 service-worker 采用的是 precache,可以通过配置 pwa.workboxPluginMode 自定义缓存策略。例如:

module.exports = {
  // ...其它 vue-cli 插件选项...
  pwa: {
    workboxPluginMode: 'InjectManifest',
    workboxOptions: {
      // swSrc 中 InjectManifest 模式下是必填的。
      swSrc: 'dev/sw.js',
      // ...其它 Workbox 选项...
    },
  },
};

vue-cli 致力于将 Vue 生态中的工具基础标准化,并确保各种构建工具能够基于智能的默认配置即可平衡衔接,提高开发效率。虽然,vue 3.0还处于开发阶段,不过相信在不久的将来一定会给开发者带来惊喜,让我们拭目以待吧。

参考: www.php.cn/js-tutorial… github.com/vuejs/vue-c… github.com/vuejs/vue-c…