做网站开封,网站托管服务商,苏州关键词优化seo,平面设计学费多少钱1 Git 1.1 Git 常见工作流程 Git 有4个区域#xff1a;工作区#xff08;workspace)、index#xff08;暂存区#xff09;、repository#xff08;本地仓库#xff09;和remote#xff08;远程仓库#xff09;#xff0c;而工作区就是指对文件发生更改的地方#xff…
1 Git 1.1 Git 常见工作流程 Git 有4个区域工作区workspace)、index暂存区、repository本地仓库和remote远程仓库而工作区就是指对文件发生更改的地方更改通过git add 存入暂存区然后由git commit 提交至本地仓库最后push推送到远程仓库。
工作目录包括.git 子目录包含仓库所有相关的 Git 修订历史记录、工作树或签出的项目文件的副本。
裸存储库只包含版本控制信息而没有工作文件工作树而且.git 子目录中的所有内容存在于主目录。
完成提交commit前称为“staging area”或“index”。此区域可对其进行格式化和审查。git 会在提交之前触发 pre-commit hook使用pre-commit hook相关的脚行对提交到存储库中的更改执行完整性检查等不满足可以阻止提交操作。
常见工作场景
初始化本地仓库 创建本地分支并推送关联远程 查看分支 合并分支 删除分支 标签管理 push变更代码的流程 开发新feature功能新建一个feature分支并关联远程分支然后合并到开发分支如果存在确定没问题再MR合并到master分支。 bug修复新建一个bugfix分支并关联远程分支然后合并到开发分支如果存在确定没问题再MR合并到master分支
git stash将工作目录修改后的跟踪文件和暂存的更改保存在一堆未完成的更改中并且可以随时重新应用这些更改。
1.2 git pull 和 git fetch
git pull 从中央存储库中提取特定分支的新更改或提交并更新本地存储库中的目标分支。
git fetch 相同的目的但工作方式略不同。 git fetch 从所需的分支中提取所有新提交存储在本地存储库中的新分支中。若要在目标分支中反映这些更改必须在 git fetch 之后执行git merge。只有在对目标分支和获取的分支进行合并后才会更新目标分支。
git pull git fetch git merge 1.4 Git 和 SVN
Git是分布式版本控制工具可本地系统克隆远端存储库支持离线提交基于C语言push/pull很快通过commit实现共享。不依赖于中央服务器来存储项目文件的所有版本。
SVN是集中版本控制工具存储在服务器仅支持在线提交push/pull较慢不支持共享。
1.5 git merge 和 git rebase
两者都是用于分支合并关键在 commit 记录的处理上不同
·git merge 会新建一个新的 commit 对象然后两个分支以前的 commit 记录都指向这个新 commit 记录。这种方法会保留之前每个分支的 commit 历史。
·git rebase 会先找到两个分支的第一个共同的 commit 祖先记录然后将提取当前分支这之后的所有 commit 记录然后将这个 commit 记录添加到目标分支的最新提交后面。经过这个合并后两个分支合并后的 commit 记录就变为了线性的记录了。而且git rebase 后git push需要添加 --force 进行强制推送。
1.6 git config
core.ignorecase是否忽略大小写默认情况下是false除了git-clone和git-init会在创建仓库时探测并适当地设置core.ignoreCase为true。如果为true这个选项可以启用各种变通方法使Git在不区分大小写的文件系统如FAT上更好地工作。
2 webpack
Webpack核心能力1. 打包压缩js混淆防止源码被利用减少网络传输2. 文件指纹合理利用http缓存3. 开发服务器热更新改善开发体验。
Webpack的好处1. 可以使用任意模块化标准无须担心兼容性问题。2. 可以将非JS视为模块使得对css、图片等资源进行更细粒度的控制。3. 前端开发也能使用npmwepack不会运行源代码而是作为依赖最终合并打包。4. 非常适合开发单页应用。
webpack会非常暴力的将public目录中除页面模板index.html的文件复制到打包结果中。
除JS和css模块外其他模块被视为资源模块。webpack是无法识别JS中直接书写的路径字符串的只有通过模块化的方式导入资源资源才会被视为模块webpack才能将该资源的原始路径转换为打包结果的真实路径。
2.1 核心概念
2.1.1 bundlechunkmodule
bundle是由webpack打包出来的可在浏览器直接运行的⽂件chunk是无法在打包结果中看到的打包结果中看到的是bundle。
chunk代码块⼀个chunk由多个模块组合⽽成⽤于代码的合并和分割简单来说它表示通过某个入口模块找到的所有依赖的统称。每个chunk都至少有两个属性
name默认是mainid开发环境和name相同生产环境是一个数字从0开始。
module是开发中的单个模块在webpack的世界⼀切皆模块⼀个模块对应⼀个⽂件webpack会从配置的 entry中递归开始找出所有依赖的模块。
module 就是没有被编译之前的代码通过 webpack 的根据文件引用关系生成 chunk 文件webpack 处理好 chunk 文件后生成运行在浏览器中的代码 bundle。
2.1.2 构建流程 Webpack 的运⾏编译、构建或打包流程是⼀个串⾏的过程从启动到结束会依次执⾏以下流程
初始化参数从配置⽂件和 Shell 语句CLI中读取与合并参数并与默认配置融合得出最终的参数依托第三方库yargs完成。开始编译⽤上⼀步得到的参数初始化 Compiler 对象加载所有配置的插件执⾏对象的 run ⽅法开始执⾏编译确定⼊⼝根据配置中的 entry 找出所有的⼊⼝⽂件编译模块从⼊⼝⽂件出发调⽤所有配置的 Loader 对模块进⾏翻译语法分析转换为AST再找出该模块依赖的模块进行记录再递归本步骤直到所有⼊⼝依赖的⽂件都经过了本步骤的处理完成模块编译在经过第4步使⽤ Loader 翻译完所有模块后得到了每个模块被翻译后的最终内容以及它们之间的依赖关系输出资源根据⼊⼝和模块之间的依赖关系组装成⼀个个包含多个模块的 Chunk再把每个 Chunk 转换成⼀个单独的⽂件加⼊到输出列表这步是可以修改输出内容的最后机会输出完成在确定好输出内容后根据配置确定输出的路径和⽂件名把⽂件内容写⼊到⽂件系统。
在以上过程中Webpack 会在特定的时间点⼴播出特定的事件插件在监听到感兴趣的事件后会执⾏特定的逻辑并且插件可以调⽤ Webpack 提供的 API 改变 Webpack 的运⾏结果。
target配置选项是构建目标指定构建出对应运行环境的代码。默认值为 browserslist如果没有找到 browserslist 的配置则默认为 web。虽然 webpack 不支持 向 target 属性传入多个字符串但是可以通过设置两个独立配置module.exports [serverConfig, clientConfig];来对 library 进行多套构建。 2.1.3 mode 模式
提供 mode 配置选项告知 webpack 使用相应模式的内置优化。Webpack4支持取值有 none | development | production默认值是 production。 1development
会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin 为所有的module源文件和chunk构建输出的文件定义一个名字。方便于浏览器调试可以快速地对增加的内容进行编译提供了更精确、更有用的运行时错误提示机制。
2production
会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。启用插件最后一个为非内置插件 FlagDependencyUsagePlugin检测并标记模块之间的从属关系FlagIncludeChunksPlugin可以让 Webpack 根据模块间的关系依赖图中将所有的模块连接成一个模块ModuleConcatenationPlugin告诉 Webapck 去清除一个大的模块文件中的未使用的代码这个大的文件模块可以是自定义的也可以是第三方的注意一定要 package.json 文件中添加 sideEffects: falseNoEmitOnErrorsPluginOccurrenceOrderPluginSideEffectsFlagPlugin告知 Webapck 各个模块间的先后顺序这样可以实现最优的构建输出TerserPlugin替代 uglifyjs-webpack-plugin 插件。它的作用依然是对构建输出的代码进行压缩
3none不使用任何默认优化选项即启动 Webpack 打包时关闭默认的内置插件。 2.1.4 entry 入口
入口起点(entry point) 指示 webpack 应该使用哪个模块来作为构建其内部依赖图(dependency graph) 的开始。进入入口起点后webpack 会找出有哪些模块和库是入口起点直接和间接依赖的。动态加载的模块不是入口起点。
Webpack 寻找相对路径的文件时解析入口点(entry point)和加载器(loader)会以 context 配置选项为根目录context 配置选项必须是绝对路径字符串。默认使用 Node.js 进程的当前工作目录但是推荐在配置中传入一个值使得配置独立于 CWD(current working directory, 当前工作目录)。 Webpack提供entry 配置选项来指定一个或多个不同的入口起点默认值是 ./src/index.js。
单入口语法string | string[]是多入口对象语法中只有一个main-chunk的简写。entry 设置为string[]在作为库的时候即和output.library一起使用时只有数组中最后一个模块会暴露。 Webpack 配置的可扩展是指配置可重用并且可以与其他配置组合使用使得可以将关注点从环境(environment)、构建目标(build target)、运行时(runtime)中分离。然后使用专门的工具如 webpack-merge将它们合并起来。
多入口对象语法entry: { entryChunkName string | [string] } | {}entry 是一个 object对象中每个属性对应一个Chunk属性名key即chunk名属性值value可以是string | string[] | {}描述每个入口的对象有如下属性
dependOn当前入口所依赖的入口它们必须在该入口被加载前被加载。使用 dependOn 选项可以与另一个入口 chunk 共享模块相当于将依赖的入口从当前入口剥离。filename指定要输出的文件名称。import启动时需加载的模块。library指定 library 选项为当前 entry 构建一个 library。runtime运行时 chunk 的名字。如果设置了就会创建一个新的运行时 chunk。publicPath当该入口的输出文件在浏览器中被引用时为它们指定一个公共 URL 地址。
确保runtime 和 dependOn 不应在同一个入口上同时使用确保dependOn 不能是循环引用的确保 runtime 不能指向已存在的入口名称否则均会抛出错误。 分离 app(应用程序) 和 vendor(第三方库) 入口的场景webpack4开始不推荐为 vendor 或其他不是执行起点创建 entry而是应该使用 optimization.splitChunks 选项将 vendor 和 app模块分开并为其创建一个单独的文件。在多页面应用程序中server会拉取一个新的 HTML 文档到客户端。页面重新加载此新文档并且资源被重新下载。借助optimization.splitChunks 为页面间共享的应用程序代码创建 bundle并对多个入口起点之间的大量代码/模块进行复用。根据经验每个 HTML 文档只使用一个入口起点。
动态入口语法entry设置为一个函数那么它将会在每次 make 事件中被调用。make 事件在 Webpack 启动和每当监听文件变化时都会触发。动态入口使得可以从外部来源远程服务器文件系统内容或者数据库获取真正的入口。 2.1.5 output 输出
output 配置选项指定webpack 对「bundle、asset 和其他所打包或使用 webpack 载入的任何内容」如何进行输出和输出位置。只能指定一个 output 配置。主要输出文件的默认值是 ./dist/main.js其他生成文件默认放置在 ./dist 文件夹中。
在 webpack 配置中output 配置选项的最低要求是包括output.filename的一个对象。
output.filename
如果webpack配置会生成多于一个 chunk例如使用多个入口起点或使用 CommonsChunkPlugin 插件则在output.filename上应该使用占位符(substitutions) 来确保每个文件具有唯一的名称。占位符可以是以下一个或多个组合
[name]chunk 名称。[id]内部chunk id从0自增。[fullhash]整个编译过程(compilation)的 hashwebpack4是[hash]已弃用。[chunkhash]每一个入口entry的hash。[contnethash]每一个模块的hash。
因为 ExtractTextWebpackPlugin 提取出来的内容是代码内容本身而不是由一组模块组成的 Chunk所以ExtractTextWebpackPlugin 插件使用 contenthash 而不是 chunkhash 来代表哈希值。
hash的长度可以使用 [chunkhash:16]默认为 20来指定或者通过指定 output.hashDigestLength 在全局配置长度
也可以使用函数 output.pathinfo
类型是boolean | verbose用于告知 webpack 在 bundle 中引入「所包含模块信息」的相关注释。此选项在 development 模式时的默认值为 true而在 production 模式时的默认值为 false。当值为 verbose 时会显示更多信息如 export以及运行时依赖。生产环境(production)下不应该使用true 或 verbose。
output.publicPath
用于指定在浏览器中所引用的「此输出目录对应的公开 URL」相对 URL(relative URL) 会被相对于 HTML 页面或 base 标签解析。相对于服务的 URL(Server-relative URL)、相对于协议的 URL(protocol-relative URL) 或绝对 URL(absolute URL) 也可能用到比如托管在CDN的资源。
该选项的值是以 runtime(运行时) 或 loader(载入时) 所创建的每个 URL 的前缀。因此大多数情况该选项的值以 / 结尾。
对于按需加载(on-demand-load)或加载外部资源(external resources)如图片、文件等来说output.publicPath 是很重要的选项。如果指定错误的值会导致404错误。
output.chunkFilename
只用于配置非初始non-initialchunk 文件即在运行时生成的chunk的名称。取值和output.filename类似。常见场景包括
使用 CommonsChunkPlugin用于创建一个单独的文件中由多个入口点之间共享的公共模块。不过从 Webpack v4 开始CommonsChunkPlugin 已被删除转而使用 optimization.splitChunks。使用 import(path/to/module) 动态加载的模块等。
output.crossOriginLoading
按需加载是通过JSONP实现的。JSONP 的原理是动态地向 HTML 中插入一个 script标签去加载异步资源。该选项配置启用 cross-origin 属性按需加载 chunk仅在 target 设置为 web 时生效。script标签的crossOrigin值
·anonymous - 不带凭据(credential) 启用跨域加载
·use-credentials - 携带凭据(credential) 启用跨域加载
默认情况下即未指定 crossOrigin 属性时CORS跨域资源共享 根本不会使用。而且script标签的可以使用cross-origin 属性来使那些将静态资源放在另外一个域名的站点打印详细错误信息。
output.library
当用 Webpack 去构建一个可以被其他模块导入使用的库时使用。类型是string | string[]| {}对象语法包括以下属性
output.library.name指定库的名称。output.library.type配置将库暴露的方式以何种方式将入口点的返回值赋值给output.library.name提供的名称之前是使用的配置选项是output.libraryTarget不推荐未来可能会废弃。取值有 var默认入口起点的返回值 将会被赋值给一个 output.library.name名称的变量。this入口起点的返回值将会被赋值给 this 对象下的 output.library.name 属性。 global入口起点的返回值将会被复制给全局对象下的 output.library.name。 commonjs入口起点的返回值将使用 output.library.name 赋值给 exports 对象。 commonjs2入口起点的返回值将会被赋值给 module.exports。 amd将库暴露为 AMD 模块。module输出ES模块。 window入口起点的返回值将会被赋值给 window 对象下的 output.library.name。output.library.export类型string | string[]指定入口起点返回值的哪一个子模块应该被暴露为一个库。默认为 undefined将会导出整个命名空间对象。string[]将被解析为一个要分配给库名的模块的路径。 2.1.6 resolve 解析
resolve配置选项用于设置模块如何被解析。类型是object。有以下几个属性
resolve.alias类型object创建 import 或 require 的别名。resolve.mainFields此选项将决定当从 npm 包中导入模块时在 package.json 中使用哪个字段导入模块默认值受target的影响。当 target 属性设置为 webworker, web 或者没有指定时resolve.mainFields的值是[browser, module, main]即会优先从package.json 的 browser 属性解析文件。对于其他任意的 target包括 node默认值为[module, main]即会优先选择package.json 的的module属性。resolve.extensions类型string[]尝试按顺序解析这些后缀名。当导入语句没带文件后缀时Webpack 会自动带上后缀后去尝试访问文件是否存在支持使用 ... 访问webpack设置的默认拓展名。
2.1.7 module包括loader
moudlue配置选项决定了如何处理项目中的不同类型的模块。Webpack 天生支持如下模块类型有 ECMAScript 模块 CommonJS 模块 AMD 模块 Assets WebAssembly 模块。
module.rules
用于配置模块的读取和解析规则通常用来配置 Loader。每个Rule规则可以分为三部分 - 条件(condition)结果(result)和嵌套规则(nested rule)。
条件匹配通过 Rule.test引入所有通过断言测试的模块、Rule.include引入符合任何条件的模块、Rule.exclude 排除所有符合条件的模块三个配置项来选中 Loader 要应用规则的文件。其中任意一个提供就不能再提供 Rule.resource。支持字符串数组和正则。
应用规则对选中的文件通过 Rule.use 配置项来应用 Loader传递字符串如use: [ style-loader ]是 loader 属性的简写方式如use: [ { loader: style-loader} ]。可以分别向 Loader 在Rule.use.options上传入参数。Rule.enforce选项设置loader的种类没有值表示是普通normal loaderpre表示前置loader相当于放到数组最后面post 表示后置loader相当于放到数组最前面。多个Loader默认按照从右往左的顺序应用因为webpack选择了compose这样的函数式编程方式这种方式的表达式执行是从右向左的。而所有一个接一个地进入的 loader都有两个阶段
Pitching 阶段loader 上的 pitch 方法从左往右的顺序调用而且如果一个patching方法返回了结果就会跳过其他的loader直接进入Normal阶段。Normal 阶段:loader 上的常规方法从右往左的顺序调用。模块源码的转换 发生在这个阶段。resolve.modules告诉 webpack 解析模块时应该搜索的目录。默认值是[node_modules]可以在之前添加目录会优先于 node_modules/ 搜索。resolve.descriptionFiles用于描述第三方模块的 JSON 文件默认值是[package.json]resolve.enforceExtension默认值是false如果是 true导入语句将不允许无扩展名文件。Webpack 4 版本只针对node_modules中模块的resolve.moduleExtensions 和 resolve.enforceModuleExtension已经被删除。
resolver解析器 是一个帮助 webpack 从每个 require/import 语句中找到需要引入到 bundle 中的模块代码的库。 当打包模块时webpack 使用 enhanced-resolve 来解析三种文件路径
绝对路径直接使用不用解析。相对路径在 import/require 中给定的相对路径会拼接使用 import 或 require 的资源文件所处的目录上下文目录来生成模块的绝对路径。模块路径在 resolve.modules 中指定的所有目录中检索模块。 可以通过 resolve.alias配置别名的方式来简化模块路径的首个目录。
一旦根据上述规则解析路径后resolver 将会检查路径是指向文件还是文件夹
如果路径指向文件如果文件具有扩展名则直接将文件打包。 否则将使用 resolve.extensions 选项作为文件扩展名来解析。如果路径指向一个文件夹则进行如下步骤寻找具有正确扩展名的文件 如果文件夹中包含 package.json 文件则会根据 resolve.mainFields 配置中的字段顺序查找并根据 package.json 中的符合配置要求的第一个字段来确定文件路径。如果不存在 package.json 文件或 resolve.mainFields 没有返回有效路径则会根据 resolve.mainFiles 配置选项中指定的文件名顺序查找看是否能在 import/require 的目录下匹配到一个存在的文件名。然后使用 resolve.extensions 选项以类似的方式解析文件扩展名。
resolveLoader配置选项与resolve配置选项的value对象的属性集相同但仅用于解析 webpack 的 loader 包。 如何编写一个loader
loader 本质上是导出为函数的 JavaScript 模块。loader runner 会调用此函数然后将上一个 loader 产生的结果或者资源文件传入进去。函数中的 this 作为上下文可以调用 loader runner 中的一些实用的方法比如可以使 loader 调用方式变为异步。编写 loader 时应该遵循以下准则
简单loaders 应该只做单一任务。这不仅使每个 loader 易维护也可以在更多场景链式调用。链式利用 loader 可以链式调用的优势写多个功能隔离的loader。loader 可以被链式调用意味着不一定要输出 JavaScript。只要下一个 loader 可以处理这个输出这个 loader 就可以返回任意类型的模块。模块化保证输出模块化。无状态确保 loader 在不同模块转换之间不保存状态。loader 工具库充分利用 loader-utils 包。
可以用 webpack-defaults 来生成开始编写 loader 必要的样板代码(boilerplate code)。
如果是单个处理结果可以在同步模式中直接return返回。如果有多个处理结果则必须调用 this.callback()且不能有return。在异步模式中必须调用 this.async() 来告知 loader runner 等待异步结果它会返回 this.callback() 回调函数。随后 loader 必须返回 undefined 并且调用该回调函数。 2.1.8 Plugin
相比loader 是对某些类型的模块进行转换插件可以用于执行范围更广的任务。包括打包优化资源管理注入环境变量等等。
webpack 插件是一个具有 apply 方 法的JavaScript Class 对象。apply 方法会被 webpack compiler 调用并且在整个编译生命周期都可以访问 compiler 对象。插件能够 hook 到编译(compilation)中发出的每一个关键事件中。
由于插件可以携带参数/选项因此必须在 webpack 配置中向 plugins 属性传入一个插件的 new 实例插件会按从左到右的顺序执行。 所有webpack内置插件都作为webpack的静态属性存在的使用则是通过new webpack.PluginName(options)。
SplitChunksPlugin
最初块Chunks及其内部导入模块是通过 Webpack 内部的依赖关系图的父子关系连接起来的用于提取公共模块。从 Webpack v4 开始CommonsChunkPlugin 已被删除转而使用 optimization.splitChunks选项配置。默认情况下 Webpack 会按照下列条件自动分割 Chunks
新的 chunk 可以被共享或者模块来自于 node_modules 文件夹。新的 chunk 体积大于 20kb在进行 mingz 之前的体积。当按需加载 chunks 时并行请求的最大数量小于或等于 30。当加载初始化页面时并发请求的最大数量小于或等于 30。 当 Webpack 处理文件路径时它们在 Unix 系统上始终包含 /在 Windows 上始终包含 \。因此必须在 cacheGroup.test 字段中使用 [\\/] 来表示路径分隔符。在跨平台使用时cacheGroups.test 中的 / 或 \ 将导致问题。
DefinePlugin
DefinePlugin 允许在编译时将代码中的变量替换为其他值或表达式。这在需要根据开发模式与生产模式进行不同的操作时非常有用。传递给 DefinePlugin 的每个键都是一个标识符或多个以 . 连接的标识符。
如果该值为字符串它将被作为代码片段来使用。如果该值不是字符串则将被转换成字符串包括函数方法。如果值是一个对象则它所有的键将使用相同方法定义。如果键添加 typeof 作为前缀它会被定义为 typeof 调用。
DllPlugin
DllPlugin 和 DllReferencePlugin 主要功能是可以将可共享且不经常改变的代码抽取成一个共享的库避免进行二次构建同时也对构建时间进行优化。
通常来说我们的代码都可以至少简单区分成业务代码和第三方库。如果不做处理每次构建时都需要把所有的代码重新构建一次耗费大量的时间。然后大部分情况下很多第三方库的代码并不会发生变更除非是版本升级这时就可以用到 dll把复用性较高的第三方模块打包到动态链接库中在不升级这些库的情况下动态库不需要重新打包每次构建只重新打包业务代码。
DllReferencePlugin 和 DllPlugin 都是在 单独的 webpack 配置中使用的用于创建一个 dll-only-bundle。建议 DllPlugin 只在 entryOnly: true 时使用否则 DLL 中的 tree shaking 将无法工作因为所有 exports 均可使用。
插件的作用是
分离代码业务代码和第三方模块可以被单独打包到不同的文件中 避免打包出单个文件的大小太大不利于调试将单个大文件拆分成多个小文件之后一定情况下有利于加载不超过浏览器一次性请求的文件数的情况下并行下载肯定比串行快提升构建速度第三方库代码没有变更时由于我们只构建业务相关代码相比全部重新构建自然要快得多
首先使用 DllPlugin 打包需要分离到动态库的模块。
然后在主构建配置文件webpack.config.js 中使用动态库文件要用到 DllReferencePlugin这个插件通过引用 dll 的 manifest 文件来把依赖的名称映射到模块的 id 上之后再在需要的时候通过内置的 webpack_require 函数来 require 它们
DllPlugin 中的 name 参数必须和 output.library 中保持一致因为DllReferencePlugin 会去 manifest.json 文件读取 name 字段的值并作为在全局变量中获取动态链接库中内容时的全局变量名。 最后在在入口文件引入 dll 文件生成的 dll 暴露出的是全局函数因此还需要在入口文件中引入对应的 dll 文件。也可以使用 AddAssetHtmlPlugin将打包好的 DLL 库引入到 HTML 模版中。
如何编写一个Plugin插件
webpack 插件由以下组成
一个 JavaScript 命名函数或 JavaScript 类。在插件函数的 prototype 上定义一个 apply 方法也就是Class里的方法。指定一个绑定到 webpack 自身的事件钩子。处理 webpack 内部实例的特定数据。功能完成后调用 webpack 提供的回调。 可以使用 schema-utils 来校验在webpack配置中传入插件的options这点和loader类似。
当hook入到的生命周期钩子函数是同步的比如compilebeforeCompile 之后立即调用但在一个新的 compilation 创建之前 阶段只有同步的 tap 方法可以使用。当hook入到的生命周期钩子函数是异步的比如run 阶段或emit输出 asset 到 output 目录之前执行阶段则可以使用 tapAsync 、 tapPromise以及tap方法。
当用 tapAsync 方法来绑定插件时必须在最后调用指定的回调函数的最后一个参数 callback 。当我们用 tapPromise 方法来绑定插件时必须返回一个 pormise异步任务完成后 resolve即可以使用async/await
有用的 plugin
clean-webpack-plugin重新构建成功之前webpack的output.path 目录中的所有文件将被删除一次但目录本身不会被删除。
html-webpack-plugin自动化的生成HTML文件这对于文件名中包含每次编译都会更改的hash的 webpack bundle特别有用。如果是多入口则对应的script标签都会包括在生成的html文件中。如果在 webpack 的输出中有任何 CSS 资源例如使用 mini-css-extract-plugin 提取的 CSS那么这些资源将包含在 HTML 头部的 link 标签中。也支持通过template选项来提供自定义模板html。通过filename选项自定义生成的html文件名。通过chunks选项自定义生成的html文件要引入哪些script标签。注意每个HtmlWebpackPlugin实例只生成一个html可以通过配置多个HtmlWebpackPlugin实例生成多个html。public/index.html 文件是一个会被 html-webpack-plugin 处理的模板。在构建过程中资源链接会被自动注入。
copy-webpack-plugin将已存在而不是构建产生的单个文件或整个目录from复制到构建目录to。如果希望 webpack-dev-server 在开发期间将文件写入输出目录可以使用 writeToDisk 选项或 write-file-webpack-plugin 强制它。
2.2 sourceMap
SourceMap 就是一个信息文件里面储存着代码的位置信息。这种文件主要用于开发调试现在代码都会经过压缩混淆这样报错提示会很难定位代码。通过 SourceMap 能快速定位到源代码并进行调试。通常情况 SourceMap 在开发环境开启线上环境关闭。
使用场景
开发期间开发人员能直接通过浏览器调试工具直接定位错误或进行 Debug 调试。线上排查问题的时候可以将 SourceMap 上传到错误监控系统。
首先浏览器一般默认开启SourceMap 调试使得控制台的 Console 面板的错误提示直接点击就会跳转到对应的源文件出错位置 如果没 Console 没报错但是页面显示不正确。可以点击控制台的 Sources 源代码tab源文件都在 webpack:// 目录下或者直接搜索文件打开源文件后进行断点调试。而对于按需加载的路由只有页面加载了源文件才会在该目录下显示。 生成sourceMap浏览器控制台才能直接显示原始代码出错的位置而不是转换后的代码使用Webpack 的devtool字段配置 sourceMap都是eval、source-map、inline、cheap 和 module 的自由组合或都没有none
none省略 devtool 选项即不生成 source map。eval使用 eval 包裹模块代码并且在末尾追加注释 //sourceURL。source-map产生 .map 文件每个 bundle 文件后缀加上 sourcemap 的 sourceURL 或 dataURL包括行列信息loader 也有对应的 sourcemap。cheapsourcemap 中的 mappings 只有对应行信息没有列文件loader 也没有对应的 sourcemap对应的都是 loader 转换后的代码不是纯正的源代码。inline将 .map 作为 DataURIBase64嵌入不单独生成 .map 文件不推荐使用因为这样会造成源代码体积巨大。module包含 loader 的 sourcemap。
开发环境推荐eval-cheap-module-source-map。
生产环境推荐
source-map应该将服务器配置为不允许普通用户访问 source map 文件。nonenosources-source-map会暴露反编译后的文件名和结构但它不会暴露原始代码。它可以用来映射客户端上的堆栈跟踪而无须暴露所有的源代码因此可以将 source map 文件部署到 web 服务器。hidden-source-map 与 source-map 相同但不会为 bundle 添加引用注释即浏览器不会主动去请求map文件。适合只想 source map 映射那些源自错误报告的错误堆栈跟踪信息但不想为浏览器开发工具暴露 source map的情况且该设置不应。
原因
使用 cheap 模式可以大幅提高 souremap 生成的效率。大部分情况调试并不关心列信息而且就算 sourcemap 没有列有些浏览器引擎例如 v8也会给出列信息。使用 eval 方式可大幅提高持续构建效率。使用 module 可支持 babel 这种预编译工具。使用 eval-source-map 模式可以减少网络请求。这种模式开启 DataUrl 本身包含完整 sourcemap 信息并不需要像 sourceURL 那样浏览器需要发送一个完整请求去获取 sourcemap 文件这会略微提高点效率。而生产环境中则不宜用 eval这样会让文件变得极大。
也可以直接使用 SourceMapDevToolPlugin/EvalSourceMapDevToolPlugin 来替代使用 devtool 选项可以进行更加精细化的控制。切勿同时使用 devtool 选项和 SourceMapDevToolPlugin/EvalSourceMapDevToolPlugin 插件。devtool 选项在内部添加过这些插件所以最终将应用两次插件。
sourceMap方便调试但也会暴露源代码存在以下安全问题
代码被抄袭查看源码抄走对应功能。业务流失竞争对手拿到源码宣扬其中的漏洞或者缺陷或者直接写后台成本少价格低抢走业务。系统被攻击绕过权限获得对应资源。
或者可以使用nosources-source-map创建的 source map 不包含 sourcesContent(源代码内容)。它可以用来映射客户端上的堆栈跟踪而无须暴露所有的源代码。你可以将 source map 文件部署到 web 服务器。
2.3 文件监听watch 和 watchOptions
Webpack 可以监听文件变化当它们修改后会重新编译。watch默认值是false即不开启监听。 2.4 模块热替换(HMR)
模块热替换 HMR-Hot Module Replacement是指在应用程序运行过程中替换、添加或删除模块而无需重新加载整个页面。主要通过以下几种方式来显著加快开发速度
保留在完全重新加载页面期间丢失的应用程序状态只更新变更内容以节省宝贵的开发时间在源代码中 CSS/JS 产生修改时会立刻在浏览器中进行更新这几乎相当于在浏览器 devtools 直接更改样式。
页面的刷新一般分为两种
一种是页面刷新直接 window.location.reload()不保留页面状态比如Live Server。基于 WDS (webpack-dev-server对应webpack命令为webpack server) 的模块热替换HMR只需要局部刷新页面上发生变化的模块同时可以保留当前的页面状态。
实现原理首先是建立起浏览器端和服务器端之间的通信浏览器会接收服务器端推送的消息如果需要热更新浏览器发起 HTTP 请求去服务器端获取打包好的资源解析并局部刷新页面橙色框是浏览器端红色框是服务端绿色方框是 Webpack 代码控制的区域蓝色方框是 webpack-dev-server 代码控制的区域。 第一步在 webpack 的 watch 模式下文件系统中某一个文件发生修改webpack 监听到文件变化根据配置文件对模块重新编译打包并将打包后的代码通过JavaScript 对象保存在内存中。第二步 webpack 和 webpack-dev-server 进行接口交互主要是 webpack-dev-server 的中间件 webpack-dev-middleware 和 webpack 之间的交互webpack-dev-middleware 调用 webpack 暴露的 API 对代码变化进行监控并且告诉 webpack将代码打包到内存中。第三步是 webpack-dev-server 对文件变化的一个监控与第一步监控代码变化重新打包不同当配置devServer.watchContentBase为true时Server会监听这些配置文件夹中静态文件的变化变化后会通知浏览器端对应用进行live reload浏览器刷新。第四步是webpack-dev-server 通过 sockjs在浏览器端和服务端之间建立一个 websocket 长连接将 webpack 编译打包的各个阶段的状态信息新模块的 Hash 值或第三步中 web-dev-server 监听静态文件变化的信息告知webpack-dev-server/client浏览器端第五步webpack-dev-server/client 端并不能够请求更新的代码也不会执⾏模块热替换的操作而是交给webpack/hot/dev-server它的⼯作就是根据 webpack-dev-server/client 传给它的信息以及 webpack-dev-server 的配置决定是刷新浏览器还是进⾏模块热替换。如果是模块热替换由HotModuleReplacement.runtime 客户端 HMR 的中枢将接收到webpack/hot/dev-server传递的新模块的 hash 值并通过JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求服务端返回⼀个 json该 json 包含了所有要更新的模块的 hash 值获取到更新列表后该模块再次通过 jsonp 请求获取到最新的模块代码图中7、8、9步。第 10 步HotModulePlugin 将会对新旧模块进⾏对⽐决定是否更新模块在决定更新模块后检查模块之间的依赖关系更新模块的同时更新模块间的依赖引⽤。是决定 HMR 成功与否的关键步骤若 HMR 失败则回退到live reload 刷新浏览器 获取最新打包代码。
2.5 webpack 相关的优化
2.5.1 ⽤webpack来优化前端性能
⽤ webpack优化前端性能是指优化webpack的输出结果让打包的最终结果在浏览器运⾏快速⾼效。
压缩代码删除多余的代码、注释、简化代码的写法等等⽅式。可以利⽤webpack的 terser-webpack-plugin默认使用 terser 来压缩 JavaScript 来压缩JS⽂件 利⽤ CssMinimizerWebpackPlugin 使用 cssnano 优化和压缩 CSS来压缩css 利用HtmlWebpackPlugin来压缩HTML。利⽤CDN加速: 在构建过程中将引⽤的静态资源路径修改为CDN上对应的路径。可以利⽤webpack对于 output 参数和各loader的 publicPath 参数来修改资源路径。Tree Shaking通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)将代码中永远不会⾛到的⽚段删除掉。可以通过在启动webpack时追加参数 --optimize-minimize 来实现。Code Splitting代码分割: 将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利⽤浏览器缓存。 入口起点使用 entry 配置手动地分离代码。防止重复使用 Entry dependencies 或者 SplitChunksPlugin 去重和分离 chunk。动态导入通过模块的内联函数import调用来分离代码。提取公共第三⽅库: SplitChunksPlugin插件来进⾏公共模块抽取利⽤浏览器缓存可以⻓期缓存这些⽆需频繁变动的公共代码。
2.5.2 提⾼webpack的构建和打包速度
1. thread-loader并⾏编译loader利⽤缓存来使得 rebuild 更快。
2. 外部扩展(externals): 将不怎么需要更新的第三⽅库脱离webpack打包不被打⼊bundle中从⽽减少打包时间⽐如生产环境下jQuery、vue、vuex、vue-router、axios等⽤script标签cdn引⼊。
3. dll: 采⽤webpack的 DllPlugin 和 DllReferencePlugin 引⼊dll让⼀些基本不会改动的代码先打包成静态资源避免反复编译浪费时间 。
4.利⽤缓存: webpack.cache 、babel-loader.cacheDirectory、thread-loader.cache 都可以利⽤缓存提⾼ rebuild效率缩⼩⽂件搜索范围。
5.多⼊⼝情况下使⽤ SplitChunksPlugin 来提取公共代码。
6. 使⽤ Tree-shaking 和 Scope Hoisting 来剔除多余代码 。
7. 使用 esbuild-loader来提升压缩速度。
8. 使用现代模式构建应用为现代浏览器交付原生支持的 ES2015 代码并生成一个兼容老浏览器的包用来自动回退。比如Vue CLI 中vue-cli-service build命令添加 --modern 选项。Vue CLI 会产生两个应用的版本一个现代版的包面向支持 ES modules 的现代浏览器另一个旧版的包面向不支持的旧浏览器仅生成一个HTML来智能的判断应该加载哪个包 1现代版的包会通过 script typemodule 在被支持的浏览器中加载它们还会使用 link relmodulepreload 进行预加载。 2旧版的包会通过 script nomodule 加载并会被支持 ES modules 的浏览器忽略。 3一个针对 Safari 10 中 script nomodule 的修复会被自动注入。
2.6 其他配置
2.6.1 externals
externals 配置选项提供了「从输出的 bundle 中排除依赖」的方法。防止将某些 import 的包(package)打包到 bundle 中而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。 3 npm
包是模块的集合用于解决某一方面的问题一个第三方库可以看作一个包每个包里面包含一个或多个模块。每个包可能有自己的github地址官网版本依赖等等。包管理器官方包管理器npm社区包管理器yarn/pnpm/cnpm所要解决的问题1. 快速的下载安装包2. 卸载包3. 优雅的升级包4. 避免版本冲突。
3.1 npm CLI
npm init创建一个package.json文件。-y/--yes跳过调查问卷。
npm install安装一个包及其依赖的任何包。依赖项的安装按npm-shrinkwrap.json package-lock.json yarn.lock的优先顺序驱动。默认情况下npm install 将任何指定的包保存到devDependencies中。-D 或 --save-dev: 包将出现在 devDependencies 中。
npm命令合集 npx运行的时候会到node_modules/.bin路径和环境变量$PATH里面检查命令是否存在可以用来运行不是全局安装或者找不到的command。Bash 内置的命令不在$PATH里面所以不能用。只要 npx 后面的模块无法在本地发现就会下载同名模块。npx 能避免全局安装模块会将模块下载到一个临时目录使用以后再删除。npx 还可以执行 GitHub 上面的模块源码前提是远程代码必须是一个模块即必须包含package.json和入口脚本。
3.2 package.json
3.2.1 必须属性
1. name: 必须小于等于214个字符不能以.或_开头不能有大写字母因为名称最终成为 URL 的一部分因此不能包含任何非URL安全字符。 npm官方建议我们不要使用与核心 node模块相同的名称。不要在名称中加 js或 node。如果需要可以使用engines来指定运行环境。name会作为参数传递给 require因此它应该是简短的但也需要具有合理的描述性。
2. version格式为 x.x.xname 和 version 一起构成一个标识符该标识符被认为是完全唯一的。每次发布时 version不能与已存在的一致。 3.2.2 描述信息
description用于编写描述信息的字符串。有助于模块在 npm库被搜索发现。keywords字符串组成的数组有助于模块在 npm库被搜索发现。homepage项目的主页地址。bugs用于反馈项目问题的 issue 地址或者邮箱。author和 contributorsauthor和 contributors均表示当前项目的共享者。contributors是对象数组具有 name字段和可选的 url及 email字段。author 可以是具有 name字段和可选的 url及 email字段的对象或由name, url和email 三部分组成的字符串edemao edemaoxx.com (https://edemao.top/)。repository指定一个源代码存放地址。 3.2.3 依赖配置
1. dependencies和devDependenciesdependencies指定项目运行所依赖的模块devDependencies指定项目开发所需要的模块。值对象的每一项为一个键值对前面是模块名称后面是对应模块的版本范围。版本号遵循“major.minor.patch”的格式规定主版本号.次版本号.修补版本号。修补版本中的更改表示不会破坏任何内容的错误修复。次要版本的更改表示不会破坏任何内容的新功能。主要版本的更改代表了一个破坏兼容性的大变化。 如果用户不适应主要版本更改则内容将无法正常工作。先行版本号及版本编译信息可以加到“主版本号.次版本号.修补版本号”的后面作为延伸1.0.0-alpha 1.0.0-alpha.1 1.0.0-alpha.beta 1.0.0-beta 1.0.0-beta.2 1.0.0-beta.11 1.0.0-rc.1 1.0.0。主版本号为零0.y.z的软件处于开发初始阶段一切都可能随时被改变。这样的公共 API 不应该被视为稳定版。
固定版本比如 5.3.1安装时只安装指定版本。波浪号比如 ~5.3.1, 表示安装 5.3.x 的最新版本不低于5.3.1但是不安装5.4.x也就是说安装时不改变大版本号和次要版本号。插入号比如 ˆ5.3.1, 表示安装 5.x.x 的最新版本不低于5.3.1但是不安装 6.x.x也就是说安装时不改变大版本号。需要注意的是如果大版本号为 0则插入号的行为与波浪号相同这是因为此时处于开发阶段即使是次要版本号变动也可能带来程序的不兼容。latest安装最新版本。
依赖安装时--save参数表示写入dependencies--save-dev表示写入devDependencies。
2. peerDependencies就是用来供插件指定其所需要的主工具的版本。比如项目依赖 A 模块和 B 模块的 1.0.0 版本而 A 模块本身又依赖 B 模块的 2.0.0 版本用 peerDepedencies 指定 A 模块 使用 B 的时候必须是 2.0.0 版本{name: A,peerDependencies: {B: 2.0.0}}。注意从npm 3.0版开始初始化的时候 peerDependencies不会默认带出。
3. bundledDependencies指定发布的时候会被一起打包的模块。
4. optionalDependencies可选的项目运行依赖写法和dependencies一样不同之处在于如果安装失败不会导致 npm install失败。
5. engines指明模块运行的平台限制比如 Node或者 npm的某个版本或者浏览器。 3.2.4 文件和目录
1. files是模块下文件名或者文件夹名构成的数组如果是文件夹名则文件夹下所有的文件也会被包含进来除非文件被另一些配置排除了。可以在模块根目录下创建一个.npmignore文件写在这个文件里边的文件即便被写在files属性里边也会被排除在外个文件的写法与.gitignore类似。
2. browser指定供浏览器使用的模块版本。指定浏览器打包工具比如 Browserify该打包的文件。
3. main指定加载的入口文件require导入的时候会加载这个文件。默认值是模块根目录下面的index.js。
4. man用来指定当前模块的 man文档的位置。
5. directoriesdirectories制定一些方法来描述模块的结构, 用于告诉用户每个目录在么位置。
6. bin用来指定每个内部命令对应的可执行文件的位置。node工具必然会用到该字段。当我们编写一个cli工具的时候需要指定工具的运行命令比如webpack执行 bin/index.js文件中的代码bin: { webpack: bin/index.js }。当模块以依赖的方式被安装如果存在bin选项会在node_modules/.bin/生成对应的文件并建立符号链接。由于node_modules/.bin/目录会在运行时加入系统的 PATH 变量所以 npm run 就可以不带路径直接通过命令来调用这些脚本文件。所有 node_modules/.bin/ 目录下的命令都可以用 npm run [命令] 的格式运行。在命令行键入npm run按tab键会显示所有可以使用的命令。
3.2.5 脚本配置
1. scripts指定了运行脚本命令的 npm 命令行缩写。使用 scripts字段可以快速的执行 shell 命令可以理解为 alias。scripts可以直接使用node_modules中安装的模块否则需要使用npx命令才能直接运行scripts: { build: webpack} // npm run build 相当于 npx webpack。
2. config用于添加命令行的环境变量。在server.js脚本就可以引用config字段的值。
也可以通过npm config set进行修改。
3.2.6 发布配置
1. license当前项目的协议—— 模块使用权限和限制。
2. os指定模块能运行的操作系统。
3. cpu限制模块只能在某种架构的cpu下运行。
4. private布尔值可以防止一个私有模块被无意间发布true则 npm拒绝发布它。
5. publishConfig在模块发布时生效用于设置发布用到的一些值的集合。如果你不想模块被默认标记 tag 为最新的或者默认发布到公共仓库可以在这里配置 tag 或仓库地址。如果只想让模块被发布到一个特定的 npm仓库通常 publishConfig会配合 private来使用。
6. preferGlobal布尔值表示当用户不将该模块安装为全局模块时即不用–global参数true 表示显示警告。 3.2.7 第三方配置
1. typings用来指定TypeScript的入口文件。
2. eslintConfigeslint的配置可以写在单独的配置文件.eslintrc.json 中也可以写在package.json文件的eslintConfig配置项中。
3. babel用来指定Babel的编译配置。
4. unpkg开启cdn服务。
5. lintstage: lint-staged是一个在Git暂存文件上运行linters的工具配置后每次修改一个文件即可给所有文件执行一次lint检查通常配合gitHooks一起使用。
6. gitHooksgitHooks用来定义一个钩子在提交commit之前执行ESlint检查。在执行lint命令后会自动修复暂存区的文件。修复之后的文件并不会存储在暂存区所以需要用git add命令将修复后的文件重新加入暂存区。在执行pre-commit命令之后如果没有错误就会执行git commit命令。
7. browserslist用来告知支持哪些浏览器及版本。Babel、Autoprefixer 和其他工具会用到它以将所需的 polyfill 和 fallback 添加到目标浏览器。 3.3 package-lock.json
package-lock.json 是对整个依赖树进行版本固定它准确的描述了当前项目npm包的依赖树并且在随后的安装中会根据 package-lock.json 来安装不考虑这个过程中是否有某个依赖有小版本的更新从而保证是依赖树不变。
注意使用cnpm install时候并不会生成 package-lock.json 文件也不会根据 package-lock.json 来安装依赖包还是会使用 package.json 来安装。
package-lock.json 可能被意外更改的原因
package.json 文件修改了挪动了包的位置将部分包的位置从 dependencies 移动到 devDependencies 这种操作虽然包未变但是也会影响 package-lock.json会将部分包的 dev 字段设置为 trueregistry 的影响安装源 registry 不同执行 npm i 时也会修改 package-lock.json
使用 npm ci来 而不是 npm i 安装依赖可以避免异常的修改 package-lock.json。
目前很多项目代码 lockfileVersion 1如果不小心更新node 14可能会导致 lockfileVersion 2而且会出现以下告警v1 npm v5 和 v6v2: npm v7v8向后兼容 v1 锁文件v3: npm v7v8 没有向后兼容性
npm WARN read-shrinkwrap This version of npm is compatible with lockfileVersion1, but package-lock.json was generated for lockfileVersion2. I’ll try to do my best with it!
解决方案需要在确定升级到 npm 8 和 package-lock.json 2 之前对 npm 版本进行降级比如npm install -g npm6.14.15。如果是在 mac 或 linux 上需要先运行rm /usr/local/bin/npm ln -s ~/.npm-packages/bin/npm /usr/local/bin/npm。
3.4.1 初始化项目 首先在github创建一个仓库协议选择MITgitignore选择Node添加README.md描述文件。使用git clone将项目克隆到本地。cd 进入目录使用vscode打开终端输入code . 命令即可。
然后创建一个合理的目录结构 3.4.2 配置typescript 3.4.3 统一代码风格
首先配置eslint使用遵循Airbnb推出的JavaScript风格指南的eslint-config-airbnbeslint-config-airbnb-typescript。 其次配置prettier安装依赖prettier.prettierrc.js的推荐配置如下 最后增加git提交校验安装依赖husky和lint-staged其中husky用于git 的hooklint-staged用于针对暂存的 git 文件运行 linters。
在package.json中配置安装 husky 的命令以及lint-staged检查 执行npm run prepare 安装 husky并在生成的.husky/pre-commit文件如果没有生成手动创建一个即可中添加 npx lint-staged 命令 All ready执行git commit 提交代码就会触发 prettier和eslint自动修复。
3.4.3 配置babel 3.4.3 配置rollup
如果开发的是工具包rollup更适合作为打包工具如果是组件比如vue组件则vue/cli 的 lib 模式更适合。根据开发环境区分不同的配置并在package.json的script中添加脚本命令输出不同规范的产物umd、umd.min、cjs、esm。
通用配置rollup.config.base.js 开发环境配置rollup.config.dev.js 正式环境配置rollup.config.prod.js 添加开发与打包相关的脚本命令到package.json借助npm-run-all 依赖npm run build按顺序执行zbuildjsbuildts。 添加支持tree shaking 的配置到package.json中。
3.4.4 发布到npm
添加发布到npm时需要忽略的文件与目录的配置即.npmignore。 添加发布相关的脚本命令到package.json中其中 npm --no-git-tag-version version 分别修改版本号中的majorminorpatch。 然后登录npm官网npm login --registry https://registry.npmjs.org/登录成功后直接发布即可npm publish --registry https://registry.npmjs.org/。
发布过程中常见的报错
400版本问题修改package.json的version即可401npm源设置成第三方源淘宝源的时候发生的比如我们经常会将镜像源设置成淘宝源。因此在发布时应该使用默认的npm源登录即npm login --registry https://registry.npmjs.org/403包名重复修改包名重新发布。
4 pnpm
pnpm 全称是 “Performant NPM”即高性能的 npm。它结合软硬链接与新的依赖组织方式大大提升了包管理的效率也同时解决了 “幻影依赖” 的问题让包管理更加规范减少潜在风险发生的可能性。
当项目中使用了一个没有在 package.json 文件中定义的包时则出现幻影依赖Phantom dependencies也称为隐式依赖或幽灵依赖。项目中缺少声明的依赖最好被一个 bug, 它可能导致一些不符合预期错误不易排查和处理
不兼容的版本项目的 package.json 声明包A包A依赖包B项目中使用到包B如果对包A进行升级而且升级的包A导致要使用升级的包B项目中所有使用到包B的地方都可能出现问题对开发者来说只是升级包A包B却出现了版本不兼容问题。缺少依赖包A是 devDependencies即开发依赖包A依赖包B项目中使用到包。对于生成环境不会安装包A的也就不会安装包B导致生产环境的依赖丢失。
5 babel
Babel 是一个 JavaScript 编译器。Babel 是一个工具链主要用于将采用 ECMAScript 2015 语法编写的代码转换为向后兼容的 JavaScript 语法以便能够运行在当前和旧版本的浏览器或其他环境中。Babel的能力
语法转换Babel也能转换JSX语法。通过 Polyfill 方式在目标环境中添加缺失的功能通过引入第三方 polyfill 模块例如 core-js。源码转换codemodsBabel 可以删除类型注释。Babel是完全插件化的。Babel 支持 Source map因此可以轻松调试编译后的代码。Babel 尽最大可能遵循 ECMAScript 标准也提供了特定的选项来对标准和性能做权衡。Babel 尽可能用最少的代码并且不依赖太大量的运行环境。也提供了assumptions选项来符合规范、文件大小和编译速度之间做权衡。
babel 的转译过程也分为三个阶段这三步具体是
解析 Parse: 将代码解析⽣成抽象语法树AST即词法分析与语法分析的过程转换 Transform: 对于 AST 进⾏⼀系列变换的操作babel 接受得到 AST 并通过 babel-traverse 对其进⾏遍历在此过程中进⾏添加、更新及移除等操作⽣成 Generate: 将变换后的 AST 再转换为 JS 代码, 使⽤到的模块是 babel/generator。
6 vite
6.1为什么选择vite Webpack: Vite: 开发服务器启动当冷启动开发服务器时基于打包器的方式启动必须优先抓取并构建整个应用然后才能提供服务。而Vite首先将应用中的模块区分为依赖和源码两类。对于依赖大多为在开发时不会变动的纯 JavaScriptVite 将会使用 esbuild 预构建依赖Go比以 Node.js 编写的打包器预构建依赖更快。对于源码通常是会被编辑且需要转换的非JavaScript文件比如jsx、tsx、css,、vue)Vite 以 原生 ESM 方式提供源码让浏览器接管了打包程序的部分工作Vite只需要在浏览器请求源码时进行转换并按需提供源码。模块热替换打包器支持了模块热替换HMR会将构建内容存入内存这样它们只需要在文件更改时使模块图的一部分失活但它也仍需要整个重新构建因此即使采用了 HMR 模式其热更新速度也会随着应用规模的增长而显著下降。而在Vite 中HMR 是在原生 ESM 上执行的。当编辑一个文件时Vite 只需要精确地使已编辑的模块与其最近的 HMR 边界大多数是模块本身之间的链接失活。使得无论应用大小如何HMR 始终能保持快速更新。缓存Vite 同时利用 HTTP 头来加速整个页面的重新加载再次让浏览器做更多事情源码模块的请求会根据 304 Not Modified 进行协商缓存而依赖模块请求则会通过 Cache-Control: max-age31536000,immutable 进行强缓存因此一旦被缓存它们将不需要再次请求。生产环境的构建原生ESM由于嵌套导入会导致额外的网络往返在生产环境中发布未打包的 ESM 仍然效率低下即使使用 HTTP/2为了在生产环境中获得最佳的加载性能最好还是将代码进行 tree-shaking、懒加载和 chunk 分割以获得更好的缓存Vite使用的是Rollup因为esbuild对于css和代码分割不是很友好。生态不如webpack
7 esbuid
esbuild 使用 go 语言编写由于相对 node 更为底层且不提供 AST 操作能力所以代码执行效率更高。可以减少等待构建运行的时间能改善开发体验但是esbuid相对较为底层因此在拉入依赖关系和配置环境上环节上会花费时间。使用Rollup 和 terser 生成的bundler包相较略小 6.8 % 左右。
esbuild 有两大功能分别是 bundler 与 minifier其中 bundler 用于代码编译类似 babel-loader、ts-loaderminifier 用于代码压缩类似 terser。 但esbuild 无法操作 AST所以一些需要操作 AST 的 babel 插件无法与之兼容导致生产环境很少直接使用 esbuild 的 bundler 模块。幸运的是 minifier 模块可以直接替换为 terser 使用可以用于生产环境。
8 eslint
ESLint 是在 ECMAScript/JavaScript 代码中识别和报告模式匹配的工具它的目标是保证代码的一致性和避免错误。在许多方面它和 JSLint、JSHint 相似除了少数的例外
ESLint 使用 Espree 解析 JavaScript。ESLint 使用 AST 去分析代码中的模式ESLint 是完全插件化的。每一个规则都是一个插件并且你可以在运行时添加更多的规则。
在目录中存在package.json文件可以使用npm init或yarn init创建的前提下运行安装和配置命令npm init eslint/config后的目录中将有一个.eslintrc.{js,yml,json}文件。