Vue脚手架文件夹结构分析
1.vue脚手架的文件结构
1.1 src源文件夹
- api
- asset
- components
- lang
- router
- store
- styles
- pages/views
- utils(工具文件夹)
- app.vue
- main.js
1.2 dist打包文件夹
1.3 其它文件
- .gitignore
- package.json
- package-lock.json
- babel.config.js
- webpack.config.js
2.如何将template转为render函数
template--->ast---->render函数
// 将模板编译为render函数
const { render, staticRenderFns } = compileToFunctions(template,options//省略}, this)
2.1 第一步:调用parse方法将template转化为ast(抽象语法树)
constast = parse(template.trim(), options)
parse的目标:把template
转换为AST
树,它是一种用 JavaScript对象的形式来描述整个模板。
解析过程:利用正则表达式顺序解析模板,当解析到开始标签、闭合标签、文本的时候都会分别执行对应的回调函数,来达到构造AST
树的目的。
2.2 第二步:对静态节点做优化
optimize(ast,options)
这个过程主要分析出哪些是静态节点,给其打一个标记,为后续更新渲染可以直接跳过静态节点做优化。深度遍历AST
,查看每个子树的节点元素是否为静态节点或者静态节点根。如果为静态节点,他们生成的DOM永远不会改变,这对运行时模板更新起到了极大的优化作用。
2.3 第三步:生成代码
const code = generate(ast, options)
generate
将AST
抽象语法树编译成 render
字符串并将静态部分放到 staticRenderFns
中,最后通过new Function(`` render``)
生成render
函数(会生成虚拟dom)。
3.runtime-only和runtime-compiler的区别
3.1 runtime-compiler
这个包里面代码量更多,是完整版的vue,包含核心功能和模板解析器(compiler
)
在代码中,可以有template
,因为complier
可以用于编译template
。
template->ast->render->vdom->ui
new Vue({
el: '#app',
...
components: { App },
template: '<App/>'
})
3.2 runtime-only
是不完整版的vue,不包含模板解析器(compiler
)。
在代码中,不可以有任何template
,只能识别render
函数。
render->vdom->ui
let vue = new Vue({
el: '#app',
...
render: h => h(App),
...
})
4.package.json和package-lock.json有什么区别?
4.1 版本号
版本由三部分组成:major.minor.patch
,即主版本号、次版本号、修补版本号。一个完整的版本号可以理解为:**[主要版本号,次要版本号,补丁版本号]。**下面是三种版本号的区别:
~
会匹配最新的小版本依赖包,如1.2.x
会匹配1.2.x
最新的版本,但是不包括1.3.0
^
会匹配最新的大版本依赖包,比如^1.2.3
会匹配1.x.x
最新的包,但是不包括2.0.0
版本*
安装最新版本的依赖包
4.2 package.json和package-lock.json
package.json
是包管理工具,里面含有name
、verson
、description
、author
、scripts
、dependencies
、devDependencies
等多个选项。下面是文件内部大致结构:
{
"name": "as-common-test-tools",
"version": "0.1.4",
"description": "前端测试工具,含有很多公共测试方法",
"main": "./src/index.js",
"keywords": [
"as-common-test-tools",
"vue",
"前端",
"工具",
"网站快速成型工具"
],
"author": "chourunfat",
"files": [
"dist"
],
"scripts": {
"serve": "vue-cli-service serve",
"build": "npm run build:js",
"lint": "vue-cli-service lint",
"build:js": "webpack --config ./webpack.config.js"
},
"dependencies": {
"core-js": "^3.8.3",
"vue": "^2.6.14",
"webpack": "^5.92.1",
"css-loader": "^7.1.2",
"style-loader": "^4.0.0"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@babel/preset-env": "^7.24.8",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"node-sass": "^4.14.1",
"sass": "^1.26.5",
"sass-loader": "^7.3.1",
"vue-loader": "^15.10.1",
"vue-template-compiler": "^2.7.16",
"webpack": "^5.92.1",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
下面重点说一下包依赖dependencies/devDependencies
,它用于对各种依赖包的管理。其中*
、^
、~
在上文中已经讲述了。我们如果直接用*
、^
、~
,看似在本地不会有问题,但如果其他人下载该项目(node_modules
不会上传到git
远程)时候,会下载当前的次版本的最新的依赖包,这可能导致新的依赖包和其它的代码发生各种问题。而package-lock.json
可以通过锁定依赖包版本来解决这个问题。
4.3 package-lock.json的由来
下面来举一个例子说明package-lock.json
的作用。
例如,有一天,我用命令行输入npm install --save-dev eslint
,立马在package.json
生成一条记录 "eslint": "^6.7.1"
。
当我们使用npm install
命令时,实际安装的npm包不一定是package.json
上的版本号,可能是6.8.x
、6.9.x
,因为以 npm 默认配置插入的记录是 ^6.7.1
(上面讲了^
的意思是会匹配最新的大版本依赖包)。
这样就会导致一个问题:每次安装的 npm 包版本可能都不一样,同时会带来一些风险。比如:6.7.1
版本的包没问题,然后最新的6.8.0
版本有bug。此时我们一位新同事把项目拉取下来之后,使用npm install
安装,然后自然会安装到6.8.0
版本,这时候可能项目在新同事电脑就跑不起来了。这种情况是会影响到我们项目的稳定性的。(尤其是不遵循语义化控制的 npm 包,强烈谴责)
我们要知道,npm install
的输入是package.json
,它的输出是一棵node_modules
树。理想情况下,npm install
应该像纯函数一样工作,对于同一个package.json
总是生成完全相同的node_modules
树。在某些情况下,确实如此。但在其他很多情况中,npm 无法做到这一点。有以下原因:
- 不同版本的 npm 的安装算法不同。
- 某些依赖项自上次安装以来,可能已发布了新版本,因此将根据
package.json
中的semver-range version
更新依赖。 - 某个依赖项的依赖项可能已发布新版本,即使您使用了固定依赖项说明符(是
"1.2.3"
而不是"^1.2.3"
),它也会更新,因为你无法固定子依赖项的版本。
而依赖项版本更新可能会带来一些问题,例如:同事A新建了一个项目,生成了上面这份 package.json
文件,但同事 A安装依赖的时间比较早,此时packageA
的最新版本是6.7.1
,该版本与代码兼容,没有出现bug。后来同事B克隆了同事 A 的项目,在安装依赖时packageA
的最新版本是 6.8.0
,那么根据语义化npm 会去安装6.8.0
的版本,但6.8.0
版本的 API 可能发生了改动,导致代码出现 bug。
这就是package.json
会带来的问题,同一份package.json
在不同的时间和环境下安装会产生不同的结果。
理论上这个问题是不应该出现的,因为npm 作为开源世界的一部分,也遵循一个发布原则:相同大版本号下的新版本应该兼容旧版本。即6.7.1
升级到6.8.0
时 API 不应该发生变化。
但很多开源库的开发者并没有严格遵守这个发布原则,导致了上面的这个问题。
为了在不同的环境下生成相同的node_modules
,于是package-lock.json
就出来了。无论何时运行 npm install
,npm 都会生成或更新package-lock.json
。
那么有了package-lock.json
之后,输入了npm install
之后有哪些过程呢?
4.4 npm install 后发生了什么
如上:
- npm5.0.x版本
不管package.json
中依赖是否有更新,npm i
都会根据package-lock.json
下载。 - npm5.1.0版本后
当package.json
中的依赖项有新版本时,npm install
会无视package-lock.json
,按照package.json
安装,并且更新到package-lock.json
。 - npm5.4.2 版本后:
当package.json
声明的依赖版本规范和package-lock.json
安装版本兼容,根据package-lock.json
安装;如果当package.json
声明的依赖版本规范和package-lock.json
安装版本不兼容,根据package-lock.json
安装,并且更新到package-lock.json
。
举个例子:如果package.json
的semver-range version
和package-lock.json
中版本兼容(package-lock.json
版本在package.json
指定的版本范围内),即使此时package.json
中有新的版本,执行npm i
也还是会根据package-lock.json
下载。如果手动修改了package.json
的version ranges
,且和package-lock.json
中版本不兼容,那么执行npm i
时package-lock.json
将会更新到兼容package.json
的版本。