Vue源码解析-响应式原理

本文深入解析Vue.js的源码,探讨响应式原理、Vue的初始化过程,包括静态成员和实例成员的初始化,以及首次渲染的流程。通过调试设置、不同构建版本的分析,帮助理解Vue的内部工作机制。
摘要由CSDN通过智能技术生成

课程目标

  • Vue.js的静态成员和实例成员的初始化过程
  • 初次渲染过程
  • 数据响应式原理

源码目录结构

在这里插入图片描述

准备工作-调试

调试设置

  • 打包

    • 打包工具 Rollup
      • Vue.js 源码的打包工具使用的是 Rollup,比 webpack 轻量
      • webpack 会把所有文件当做模块,Rollup 只处理 js 文件,更适合在 Vue.js 这样的库中使用(开发项目使用 webpack,开发库使用 Rollup)
      • Rollup打包不会生成冗余的代码
  • 安装依赖

npm i 
  • 设置sourcemap
    • package.json 文件的 dev 脚本中添加参数 --sourcemap
"dev": "rollup -w -c scripts/config.js  --sourcemap --environment TARGET:web-full-dev"
  • 执行dev
    • npm run dev 执行打包,用的是rollup, -w 参数是监听文件的变化,文件变化自动重新打包; -c是执行的配置文件
  • 结果
    在这里插入图片描述
  • 以vue源码中examples中的grids为例进行调试
    在这里插入图片描述
  • 在进行代码调试的时候,如果没有开启sourcemap,则不会生成src目录(实际上是dist中的map文件指向src),断点不会进入src(源码),而直接进入打包后(dist)目录中压缩的vue.js文件,因为是压缩,编译后的代码,不方便调试,而我们希望断点直接进入src目录下的源码中调试,所以需要设置sourcemap

准备工作-Vue的不同构建版本

  • npm run build 重新打包所有文件
  • 官方文档-对不同构建版本的解释
  • 完整版:同时包含编译器和运行时的版本。
  • 编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码。=>将template装换成render函数
  • 运行时:用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码。基本上就是除去编译器的其它一切。

完整版Vue举例

<div id="app"></div>
    <!-- 完整版 -->
    <script src="../../dist/vue.js"></script>
    <script>
      const vm = new Vue({
   
        el: "#app",
        template: "<h1>{
   {msg}}</h1>",
        data: {
   
          msg: "Hello Vue",
        },
      });
    </script>
  • 结果:可以正常显示
    在这里插入图片描述
<div id="app"></div>
    <!-- 运行时版本 -->
    <script src="../../dist/vue.runtime.js"></script>
    <script>
      const vm = new Vue({
   
        el: "#app",
        template: "<h1>{
   {msg}}</h1>",
        data: {
   
          msg: "Hello Vue",
        },
      });
    </script>
  • 结果:报错
    在这里插入图片描述
  • 使用的是Vue的仅运行时版本,其中模板编译器不可用。方法一:将模板手动预编译为render函数,方法二:使用带编译器的vue版本
  • 方法二就是上面的引用完整版本的vue,下面介绍方法一
  • 方法一,将template模板字符串转换成render函数
<div id="app"></div>
    <!-- 运行时版本 -->
    <script src="../../dist/vue.runtime.js"></script>
    <script>
      const vm = new Vue({
   
        el: "#app",
        render(h) {
   
          return h("h1", this.msg);
        },
        data: {
   
          msg: "Hello Vue",
        },
      });
  • 结果:能正常显示

小结

  • vue-cli默认引用的是运行时版本(不带编译器),并且是ESModule=>vue.runtime.esm.js
  • 因为vue-cli对webpack做了一个深度的封装,我们在vue-cli创建的项目中,看不到引入vue的版本,但vue-cli提供的一个命令行工具,通过这个工具可以查看webpack的配置
  • 命令行输入vue inspect
    在这里插入图片描述
  • webpack配置项在命令行中显示查看起来不太友好,直接输出到文件中(>代表把前面命令生成的结果输入到指定文件中)
vue inspect > output.js

在这里插入图片描述

  • 我们在开发项目的时候,会有很多单文件组件(.vue文件),这些单文件组件浏览器是不支持的,所以在打包的时候我们会将这些单文件组件转换成js对象,在转换js对象的过程中,还会将template模板转换成render函数(vue-loader来实现),所以单文件组件的运行是不需要编译器的

寻找入口文件

  • 查看dist/vue.js的构建过程

执行构建

npm run build
"build": "node scripts/build.js",
  • 通过node 生成所有版本的vue
npm run dev
"dev": "rollup -w -c scripts/config.js  --sourcemap --environment TARGET:web-full-dev"
  • rollup:通过rollup构建工具生成单一的vue版本
  • environment:设置环境变量TARGET:web-full-dev
    • web:生成web平台的vue版本
    • full:生成完整版的vue(含编译器+运行时)
    • dev:开发环境下的vue版本(不压缩)
  • script/config.js 的执行过程
    • 作用:生成rollup构建的配置文件
    • 使用环境变量TARGET:web-full-dev

script/config.js文件执行过程

  • 以npm run dev 为 例
"dev": "rollup -w -c scripts/config.js  --sourcemap --environment TARGET:web-full-dev",
const path = require('path')
const buble = require('rollup-plugin-buble')
const alias = require('rollup-plugin-alias')
const cjs = require('rollup-plugin-commonjs')
const replace = require('rollup-plugin-replace')
const node = require('rollup-plugin-node-resolve')
const flow = require('rollup-plugin-flow-no-whitespace')
const version = process.env.VERSION || require('../package.json').version
const weexVersion = process.env.WEEX_VERSION || require('../packages/weex-vue-framework/package.json').version
const featureFlags = require('./feature-flags')

const banner =
  '/*!\n' +
  ` * Vue.js v${
     version}\n` +
  ` * (c) 2014-${
   new Date().getFullYear()} Evan You\n` +
  ' * Released under the MIT License.\n' +
  ' */'

const weexFactoryPlugin = {
   
  intro () {
   
    return 'module.exports = function weexFactory (exports, document) {'
  },
  outro () {
   
    return '}'
  }
}

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)
  }
}

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': {
   
    // resolve函数将相对路径转换成绝对路径(web是别名)
    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')
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值