[Чтение исходного кода vue] Что делает импорт Vue из 'vue'?

Vue.js

Чтение ресурсов

Адрес хостинга исходного кода vue.js

Адрес инструмента статической проверки потока

накопительная исходная сборка

инструмент статической проверки потока

Flow - это инструмент проверки статического типа JavaScript, производимый Facebook. Исходный код Vue.js использует поток для проверки статического типа. Потому что JavaScript - это динамически напечатанный язык. Хотя язык гибкий, легко вызвать скрытые скрытые скрытые коды. Кажется, не сообщает о ошибках при компиляции, но при запуске появляются странные ошибки. Проверка типа - найти ошибки, вызванные ошибками типа, как можно раньше при компиляции, не влияя на выполнение кода (не нужно динамически проверять типы во время выполнения). Использование инструментов для некоторых сложных проектов может повысить читаемость кода и повысить ремонтопригодность проекта.

Существует два типа проверки типа потока:

  • Вывод типа: тип переменной выводится из контекста, в котором используется переменная, и тип проверяется на соответствие выводу.
  • Аннотация типа: аннотируйте тип переменной и проверьте тип с помощью аннотации.
// 类型推断
// @flow
function split(str) {
    return str.split(' ')
}
split(1)      // Error!
split('abc')  // Works!

1, когда входящие параметры, проверка кода потока будет ошибкой, потому чтоsplit()Метод представляет собой метод объекта-прототипа строки. Ожидаемый тип параметра — строка, а входящий параметр — действительно число, поэтому будет сообщено об ошибке. Когда передается строка 'abc', код может выполняться в обычном режиме.

// 类型注释
// @flow
function concat(a: string, b: string) {
  return a + b;
}

concat("A", "B");   // Works!
concat(1, 2);       // Error!

Благодаря оператору сложения он может выполнять как сложение чисел, так и объединение строк. такconcat()Метод заранее аннотируется типом параметра. Полученные параметры a и b являются строками, поэтому вызовитеconcat(1, 2);сообщит об ошибке. передачаconcat("A", "B")Он будет выполняться в обычном режиме.

Обратите внимание, что: когда появится комментарий файла@flowОфициальный документ инструмента статической проверки потока.

Приложение Flow в исходном коде vue.js

В основном каталоге Vue.js есть.flowconfigфайл, который является файлом конфигурации для Flow. следующим образом:

[ignore]  // 忽略的文件
.*/node_modules/.*
.*/test/.*
.*/scripts/.*
.*/examples/.*
.*/benchmarks/.*

[include]

[libs] // 这里 [libs] 配置的是 flow,表示指定的库定义都在 flow 文件夹内
flow   // 对应的 flow 目录

[options]
unsafe.enable_getters_and_setters=true
module.name_mapper='^compiler/\(.*\)$' -> '<PROJECT_ROOT>/src/compiler/\1'
module.name_mapper='^core/\(.*\)$' -> '<PROJECT_ROOT>/src/core/\1'
module.name_mapper='^shared/\(.*\)$' -> '<PROJECT_ROOT>/src/shared/\1'
module.name_mapper='^web/\(.*\)$' -> '<PROJECT_ROOT>/src/platforms/web/\1'
module.name_mapper='^weex/\(.*\)$' -> '<PROJECT_ROOT>/src/platforms/weex/\1'
module.name_mapper='^server/\(.*\)$' -> '<PROJECT_ROOT>/src/server/\1'
module.name_mapper='^entries/\(.*\)$' -> '<PROJECT_ROOT>/src/entries/\1'
module.name_mapper='^sfc/\(.*\)$' -> '<PROJECT_ROOT>/src/sfc/\1'
suppress_comment= \\(.\\|\n\\)*\\$flow-disable-line

Описание каталога Flow в vue.js

flow
├── compiler.js        # 编译相关
├── component.js       # 组件数据结构
├── global-api.js      # Global API 结构
├── modules.js         # 第三方库定义
├── options.js         # 选项相关
├── ssr.js             # 服务端渲染相关
├── vnode.js           # 虚拟 node 相关

Структура каталогов исходного кода vue

Исходный код Vue.js находится в каталоге src, а структура каталогов выглядит следующим образом:

src
├── compiler    # 包含 Vue.js 所有编译相关的代码。
├── core        # 包含了 Vue.js 的核心代码,包括内置组件、全局 API 封装,Vue 实例化、观察者、虚拟 DOM、工具函数等等。
├── platforms   # Vue.js 是一个跨平台的 MVVM 框架,它可以跑在 web 上,也可以配合 weex 跑在 native 客户端上。
├── server      # 所有服务端渲染相关的逻辑都在这个目录下。
├── sfc         # vue.js通过 .vue 单文件来编写组件。这个目录下的代码逻辑会把 .vue 文件内容解析成一个 JavaScript 的对象。
├── shared      # 定义一些工具方法,这里定义的工具方法都是会被浏览器端的 Vue.js 和服务端的 Vue.js 所共享的。

С точки зрения всей структуры каталога автор разбирает функциональные модули очень четко, и связанная логика размещена в том же каталоге для обслуживания. Многоразовый код также является отдельной папкой.

Создание исходного кода Vue.js

Исходный код Vue.js основан на Rollup, а его конфигурация, связанная со сборкой, находится в каталоге scripts. Rollup — это инструмент для упаковки модулей javascript. Легче вебпака. Узнайте больше, пожалуйста, посетитеСводный адрес Github

скрипт сборки

Проекты, размещенные в NPM, будут иметь файл package.json, описывающий проект.scriptПоля используются для определения сценария, который будет выполняться NPM. Скрипт, выполняющий сборку vue.js, выглядит следующим образом:

"scripts": {
    "build": "node scripts/build.js",
    "build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
    "build:weex": "npm run build -- weex",
  },

То есть, когда мы выполняемnpm run build, фактически выполняетnode scripts/build.jsЭто утверждение. то естьscripts/build.jsЭто для создания записи файла js.

процесс сборки

1. Начните с файла записи сборки:scripts/build.js

let builds = require('./config').getAllBuilds() //拿到所有的配置

// filter builds via command line arg
if (process.argv[2]) {
  const filters = process.argv[2].split(',')
  builds = builds.filter(b => {
    return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1)
  })
} else {
  // filter out weex builds by default
  builds = builds.filter(b => {
    return b.output.file.indexOf('weex') === -1
  })
}

build(builds)

Приведенная выше часть кода, сначала из файла конфигурацииscripts/config.jsПрочитайте данные конфигурации, относящиеся к конфигурации соответствующего фильтра, для создания различных целей vue.js.

2. Просмотрите файл конфигурации сборки:scripts/config.js

const builds = {
  // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
  'web-runtime-cjs-dev': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.dev.js'),
    format: 'cjs',
    env: 'development',
    banner
  },
  'web-runtime-cjs-prod': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.prod.js'),
    format: 'cjs',
    env: 'production',
    banner
  },
  // Runtime+compiler CommonJS build (CommonJS)
  'web-full-cjs-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.common.dev.js'),
    format: 'cjs',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  },
  'web-full-cjs-prod': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.common.prod.js'),
    format: 'cjs',
    env: 'production',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime only ES modules build (for bundlers)
  'web-runtime-esm': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.esm.js'),
    format: 'es',
    banner
  },
  // Runtime+compiler ES modules build (for bundlers)
  'web-full-esm': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.js'),
    format: 'es',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime+compiler ES modules build (for direct import in browser)
  'web-full-esm-browser-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.browser.js'),
    format: 'es',
    transpile: false,
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime+compiler ES modules build (for direct import in browser)
  'web-full-esm-browser-prod': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.browser.min.js'),
    format: 'es',
    transpile: false,
    env: 'production',
    alias: { he: './entity-decoder' },
    banner
  },
  // runtime-only build (Browser)
  'web-runtime-dev': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.js'),
    format: 'umd',
    env: 'development',
    banner
  },
  // runtime-only production build (Browser)
  'web-runtime-prod': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.min.js'),
    format: 'umd',
    env: 'production',
    banner
  },
  // Runtime+compiler development build (Browser)
  'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime+compiler production build  (Browser)
  'web-full-prod': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.min.js'),
    format: 'umd',
    env: 'production',
    alias: { he: './entity-decoder' },
    banner
  },
  // Web compiler (CommonJS).
  'web-compiler': {
    entry: resolve('web/entry-compiler.js'),
    dest: resolve('packages/vue-template-compiler/build.js'),
    format: 'cjs',
    external: Object.keys(require('../packages/vue-template-compiler/package.json').dependencies)
  },
  // Web compiler (UMD for in-browser use).
  'web-compiler-browser': {
    entry: resolve('web/entry-compiler.js'),
    dest: resolve('packages/vue-template-compiler/browser.js'),
    format: 'umd',
    env: 'development',
    moduleName: 'VueTemplateCompiler',
    plugins: [node(), cjs()]
  },
  // Web server renderer (CommonJS).
  'web-server-renderer-dev': {
    entry: resolve('web/entry-server-renderer.js'),
    dest: resolve('packages/vue-server-renderer/build.dev.js'),
    format: 'cjs',
    env: 'development',
    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
  },
  'web-server-renderer-prod': {
    entry: resolve('web/entry-server-renderer.js'),
    dest: resolve('packages/vue-server-renderer/build.prod.js'),
    format: 'cjs',
    env: 'production',
    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
  },
  'web-server-renderer-basic': {
    entry: resolve('web/entry-server-basic-renderer.js'),
    dest: resolve('packages/vue-server-renderer/basic.js'),
    format: 'umd',
    env: 'development',
    moduleName: 'renderVueComponentToString',
    plugins: [node(), cjs()]
  },
  'web-server-renderer-webpack-server-plugin': {
    entry: resolve('server/webpack-plugin/server.js'),
    dest: resolve('packages/vue-server-renderer/server-plugin.js'),
    format: 'cjs',
    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
  },
  'web-server-renderer-webpack-client-plugin': {
    entry: resolve('server/webpack-plugin/client.js'),
    dest: resolve('packages/vue-server-renderer/client-plugin.js'),
    format: 'cjs',
    external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
  },
  // Weex runtime factory
  'weex-factory': {
    weex: true,
    entry: resolve('weex/entry-runtime-factory.js'),
    dest: resolve('packages/weex-vue-framework/factory.js'),
    format: 'cjs',
    plugins: [weexFactoryPlugin]
  },
  // Weex runtime framework (CommonJS).
  'weex-framework': {
    weex: true,
    entry: resolve('weex/entry-framework.js'),
    dest: resolve('packages/weex-vue-framework/index.js'),
    format: 'cjs'
  },
  // Weex compiler (CommonJS). Used by Weex's Webpack loader.
  'weex-compiler': {
    weex: true,
    entry: resolve('weex/entry-compiler.js'),
    dest: resolve('packages/weex-template-compiler/build.js'),
    format: 'cjs',
    external: Object.keys(require('../packages/weex-template-compiler/package.json').dependencies)
  }
}

Приведенный выше код представляет собой конфигурацию сборки vue.js, подключаемого модуля webpack для рендеринга на стороне сервера и конфигурации упаковки weex. Для одиночной конфигурации соблюдаются правила сборки Rollup. Инструкции по настройке:

  • атрибут записи: адрес js-файла записи сборки.
  • Атрибут dest: адрес js-файла после завершения сборки.
  • Свойство формата: формат файла сборки. «cjs» указывает, что созданный файл соответствует спецификации CommonJS; «es» указывает, что созданный файл соответствует спецификации модуля ES; «umd» указывает, что созданный файл соответствует спецификации UMD.
  • свойство баннера: краткое описание vue.js. Содержит информацию об авторе, номер версии и т. д.

3. Возьмите конфигурацию в качестве примера, чтобы изучить процесс сборки:web-runtime-cjs

'web-runtime-cjs-dev': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.dev.js'),
    format: 'cjs',
    env: 'development',
    banner
  },

Видно из конфигурации: называется адрес js файла записи и заполненный js адресresolve()метод.

const aliases = require('./alias')
const resolve = p => {
  const base = p.split('/')[0]
  if (aliases[base]) {
    return path.resolve(aliases[base], p.slice(base.length + 1))
  } else {
    return path.resolve(__dirname, '../', p)
  }
}

resolve()Метод будет вызываться с параметром p, переданным вsplit()метод, разделенный на массив с помощью '/', затем возьмите первый элемент и установите его какbaseзатем в приведенном выше случаеbaseто естьweb. ноbaseНе реальный путь, а конфигурация псевдонима. Код для настройки псевдонима выглядит следующим образом:scripts/alias

const path = require('path')

const resolve = p => path.resolve(__dirname, '../', p)
// 到真实文件的一个映射关系
module.exports = {
  vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
  compiler: resolve('src/compiler'),
  core: resolve('src/core'),
  shared: resolve('src/shared'),
  web: resolve('src/platforms/web'),
  weex: resolve('src/platforms/weex'),
  server: resolve('src/server'),
  sfc: resolve('src/sfc')
}

Из приведенного выше кода видно, что:webСоответствующий путь знанияpath.resolve(__dirname, '../', 'src/platforms/web'). Входной файл, чтобы найти его из этогоsrc/platforms/web/entry-runtime.jsПосле сборки и упаковки с помощью Rollup он в конечном итоге будетdist/vue.runtime.common.js.

Runtime Only VS Runtime + Compiler

Обычно, когда мы используем vue-cli для инициализации нашего проекта Vue.js, мы спрашиваем, следует ли использоватьRuntime Onlyверсия все ещеRuntime + CompilerВерсия. Их отличия заключаются в следующем:

  • Runtime Only обычно требует помощи инструментов vue-loader, таких как webpack, для.vueФайл компилируется в JavaScript, а шаблон компилируется в функцию рендеринга. Поскольку это делается на фазе компиляции, он содержит только код Vue.js во время выполнения, поэтому размер кода будет легче.

  • Runtime + компилятор Если мы не сделали предварительно скомпилированного кода, но используйте атрибут шаблона Vue и пропустите строку, вам необходимо скомпилировать шаблон на стороне клиента, следующим образом:

// 需要编译器的版本
new Vue({
  template: '<div>{{ hi }}</div>'
})

// 这种情况不需要
new Vue({
  render (h) {
    return h('div', this.hi)
  }
})

Находка: потому что в Vue.js 2.0 окончательный рендеринг передаетсяrenderфункция, если вы пишетеtemplateсвойства, вам нужно скомпилировать вrenderфункция, то этот процесс компиляции будет происходить во время выполнения, поэтому требуется версия с компилятором. Очевидно, что этот процесс компиляции будет иметь определенную потерю производительности, поэтому рекомендуется использоватьRuntime Only.

Вход в Вью

когда мы разрабатываемimport Vue from 'vue'Что он сделал? Запись vue.js, созданная только во время выполнения, находится по адресуsrc/platforms/web/entry-runtime.jsкод показывает, как показано ниже:

/* @flow */

import Vue from './runtime/index'

export default Vue 

Приведенный выше код экспортирует Vue, и этот Vue получен из./runtime/indexимпортный.

Статическая глобальная конфигурация и методы vue для объектов-прототипов

Продолжать смотреть./runtime/indexдокумент. код показывает, как показано ниже:

/* @flow */

import Vue from 'core/index'
import config from 'core/config'
import { extend, noop } from 'shared/util'
import { mountComponent } from 'core/instance/lifecycle'
import { devtools, inBrowser } from 'core/util/index'

import {
 query,
 mustUseProp,
 isReservedTag,
 isReservedAttr,
 getTagNamespace,
 isUnknownElement
} from 'web/util/index'

import { patch } from './patch'
import platformDirectives from './directives/index'
import platformComponents from './components/index'

// install platform specific utils
// 静态的全局配置
Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement

// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)

// install platform patch function
// 原型__patch__
Vue.prototype.__patch__ = inBrowser ? patch : noop

// public mount method
// 定义了原型上的$mount 方法
Vue.prototype.$mount = function (
 el?: string | Element,
 hydrating?: boolean
): Component {
 el = el && inBrowser ? query(el) : undefined
 return mountComponent(this, el, hydrating)
}

// devtools global hook
/* istanbul ignore next */
if (inBrowser) {
 setTimeout(() => {
   if (config.devtools) {
     if (devtools) {
       devtools.emit('init', Vue)
     } else if (
       process.env.NODE_ENV !== 'production' &&
       process.env.NODE_ENV !== 'test'
     ) {
       console[console.info ? 'info' : 'log'](
         'Download the Vue Devtools extension for a better development experience:\n' +
         'https://github.com/vuejs/vue-devtools'
       )
     }
   }
   if (process.env.NODE_ENV !== 'production' &&
     process.env.NODE_ENV !== 'test' &&
     config.productionTip !== false &&
     typeof console !== 'undefined'
   ) {
     console[console.info ? 'info' : 'log'](
       `You are running Vue in development mode.\n` +
       `Make sure to turn on production mode when deploying for production.\n` +
       `See more tips at https://vuejs.org/guide/deployment.html`
     )
   }
 }, 0)
}

export default Vue

Приведенный выше код все еще изcore/indexИмпортируйте Vue в файл и, наконец, экспортируйте его. В этом файле определены некоторые статические глобальные конфигурации Vue и методы объекта-прототипа.

Добавьте статические методы для vue через initGlobalAPI

продолжай смотреть внизcore/indexКак определить Vue, код выглядит следующим образом:

import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'

initGlobalAPI(Vue) // 定义了vue 本身的静态方法

Object.defineProperty(Vue.prototype, '$isServer', {
 get: isServerRendering
})

Object.defineProperty(Vue.prototype, '$ssrContext', {
 get () {
   /* istanbul ignore next */
   return this.$vnode && this.$vnode.ssrContext
 }
})

// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
 value: FunctionalRenderContext
})

Vue.version = '__VERSION__'

export default Vue

Точно так же это от./instance/indexИмпортируйте VUE в файл и, наконец, экспортируйте его. Этот файл добавляет статический метод в Vue через метод initglobalAPI.

Добавление методов к прототипу Vue через миксины

продолжить вниз к./instance/indexфайл, код такой:

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'


// 终于溯源结束了,Vue就是一个用 Function 实现的类,所以才通过 new Vue 去实例化它。
function Vue (options) {
 if (process.env.NODE_ENV !== 'production' &&
   !(this instanceof Vue)
 ) {
   warn('Vue is a constructor and should be called with the `new` keyword')
 }
 this._init(options)
}

// 在vue原型上挂了方法
initMixin(Vue) 
stateMixin(Vue) 
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

На этом прослеживаемость, наконец, закончилась.Vue — это класс, реализованный с помощью функции., так что только черезnew VueИди создай экземпляр. Файл добавляется к прототипу Vue с помощью Mixin.

конец

Недавно я серьезно посмотрю на исходный код vue.js. [Читать исходный код vue] будет обновляться в соответствии с серией. Делясь собственными знаниями, я также надеюсь общаться с большим количеством сверстников, вот и все.

первый раз:[Чтение исходного кода vue] Что делает импорт Vue из 'vue'?【В настоящее время читаю】

Вторая статья:[Читать исходный код vue] Узнайте, как шаблоны и данные отображаются в DOM?