写在前面

最近新部门的 React 项目要做多环境部署,参考了一下之前部门做的 Vue 项目,顺便把 vue-cli-service 的源码看了一下。看源码的时候还去看了下 Vue 源码,感觉挺有意思的,打算好好研究下,这里持续更新本人的心得体会~

vue-cli-service多环境

首先在 package.json 里面 script 下面添加如下内容:

"scripts": {
    "serve": "vue-cli-service serve",
    "serve:staging": "vue-cli-service serve --mode staging",
    "serve:prod": "vue-cli-service serve --mode production",
    "lint": "vue-cli-service lint",
    "format": "vue-cli-service lint --formatter",
    "inspect": "vue-cli-service inspect",
    "build": "vue-cli-service build",
    "build:staging": "vue-cli-service build --mode staging",
    "build:prod": "vue-cli-service build --mode production"
},

vue-cli-service 源码在 node_modules/@vue/cli-service/bin/vue-cli-service.js 目录下

vue-cli-service.js 在第 14 行加载了 ../lib/Service.js 模块,这个模块在 151 行加载了 serve.js 模块

serve.js 在 node_modules/@vue/cli-service/lib/commands/serve.js 目录下

// serve.js 第 50 行
// 检测环境,只在开发环境引入热更新插件
// webpack配置链式语法可以看一下
// configs that only matters for dev server
api.chainWebpack(webpackConfig => {
    if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
        webpackConfig
            .devtool('cheap-module-eval-source-map')

        webpackConfig
            .plugin('hmr')
            .use(require('webpack/lib/HotModuleReplacementPlugin'))

        // https://github.com/webpack/webpack/issues/6642
        // https://github.com/vuejs/vue-cli/issues/3539
        webpackConfig
            .output
            .globalObject(`(typeof self !== 'undefined' ? self : this)`)

        if (!process.env.VUE_CLI_TEST && options.devServer.progress !== false) {
            webpackConfig
                .plugin('progress')
                .use(require('webpack/lib/ProgressPlugin'))
        }
    }
})

vue-cli和creat-react-app打开浏览器的插件

Vue 中 openBrowser 的插件在 node_modules/@vue/cli-shared-utils 目录下。打开 index.js 可以看到如下内容:

34b3eaa1b92945b86554aa7f1a68db05.png

这边引入了 openBrowser.js 文件,这个文件在 lib 目录下,打开可以看到如下内容:

23a680a8d7ba9de85e999dfde2d1403b.png

这个插件跟 React 里面的 openBrowser.js 是一样的,React相关代码如下:

76eaa6f08e5f177fe5d2448d85e08758.png

Vue 检测数组变化

我们知道 Vue 2.x 版本的响应式机制是通过 Object.defineProperty 实现的,这种方式不能够检测数组长度的变化,只能将数组整个替换掉才会触发 setter。但是在实际开发中,调用数组的非变异方法(push,pop,shift,unshift,splice,sort,reverse),也能触发视图更新,这是因为 Vue 对这些方法进行了封装。

源码在 node_modules/vue/src/core/observer/array.js

import { def } from '../util/index'

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)

const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
  // cache original method
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    // notify change
    ob.dep.notify()
    return result
  })
})

其中 def() 的作用就是重新定义对象属性的value值,代码在 util/lang.js

/**
 * Define a property.
 */
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
  })
}

def(arrayMethods,method,function(){}) 这个函数可以看作 arrayMethods[method] = function mutator(){}。具体可以看下面的例子:

const push = Array.prototype.push;
 
Array.prototype.push = function mutator (...arg){
  const result = push.apply(this,arg);
  doSomething();
  return result
}
 
function doSomething(){
  console.log('do something');
}

首先第一步就是保存了原生的数组方法,先求出实际的值

注意:为触发视图更新,应该将数组整个替换/调用变异方法,或者使用非变异方法。直接用下标修改数组或者用 length 修改是无法被检测到的。

$nextTick原理

Vue 核心源码都在 node_modules/vue/src/core 目录下

这个目录下还包括 vdom ,observer ,global-api 的实现,都可以看一下

$nextTick 的源码在 node_modules/vue/src/core/util/next-tick.js 目录下


一杯绿茶
199 声望17 粉丝

人在一起就是过节,心在一起就是团圆