内容简介:自己配置过webpack的人应该都知道,webpack真的好复杂,一开始做项目都是拿别人现成的做做小修改,但是别人的终究没有自己配的舒服。所以我打算写这篇文章,从我的配置中带大家了解webpack配置,简化读webpack官方页面的复杂度。首先我们需要明确我们要求webpack做什么?1.所写即所得,我们在编译器中写了代码能马上呈现在调试器上(热更新服务)
自己配置过webpack的人应该都知道,webpack真的好复杂,一开始做项目都是拿别人现成的做做小修改,但是别人的终究没有自己配的舒服。所以我打算写这篇文章,从我的配置中带大家了解webpack配置,简化读webpack官方页面的复杂度。
首先我们需要明确我们要求webpack做什么?
1.所写即所得,我们在编译器中写了代码能马上呈现在调试器上(热更新服务)
2.本地开发获取数据存在的跨域问题(代理,解决跨域)
3.使用es678,less,sass等(翻译,让浏览器懂得我们的代码)
4.提高项目性能,比如压缩代码,压缩图片等(项目优化)
5.如果与输入相关的需求,找entry(比如多页面就有多个入口) 6.如果与输出相关的需求,找output(比如你需要定义输出文件的路径、名字等等) 7.如果与模块寻址相关的需求,找resolve(比如定义别名alias) 8.如果与转译相关的需求,找loader(比如处理sass处理es678N) 9.如果与构建流程相关的需求,找plugin(比如我需要在打包完成后,将打包好的文件复制到某个目录,然后提交到git上) 接下来我们来看下webpack的一些基本配置
1.entry(项目入口)
2.output(出口文件)
3.modules(模块处理)
4.plugin(插件)
5.resolve
6.devserver
7.mode,
8devtool
1.项目入口设置(entry)
主要有三种方式
1.字符串形式
entry: '.src/main.js' 复制代码
2.数组形式
entry: [react, react-dom] 复制代码
3.对象形式
entry: { main:'./src/index2.js', second: './src/index2.js', vendor:['react', 'react-dom'] } 复制代码
2.出口文件设置及(output)
const path = require('path'); module.exports = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js' } }; 复制代码
在上面的示例中,我们通过 output.filename 和 output.path 属性,来告诉 webpack bundle 的名称,以及我们想要 bundle 生成(emit)到哪里。 Path.resolve是什么,引入的path模块是干什么用的
Nodejs
该path.resolve()方法将一系列路径或路径段解析为绝对路径。
给定的路径序列从右到左处理,随后每个path路径都被预先加载,直到构造出绝对路径。例如,给定路径段的序列:/foo,/bar,baz,调用path.resolve('/foo', '/bar', 'baz')将返回/bar/baz。
如果在处理path完所有给定段之后尚未生成绝对路径,则使用当前工作目录。 生成的路径已规范化,并且除非将路径解析为根目录,否则将删除尾部斜杠。 零长度path段被忽略。
如果没有path传递段,path.resolve()将返回当前工作目录的绝对路径。
path.resolve('/foo/bar', './baz'); // Returns: '/foo/bar/baz' path.resolve('/foo/bar', '/tmp/file/'); // Returns: '/tmp/file' path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif'); // If the current working directory is /home/myself/node, // this returns '/home/myself/node/wwwroot/static_files/gif/image.gif' _dirname 复制代码
当前模块的目录名称。这是一样 path.dirname()的__filename。
示例:node example.js从中运行/Users/mjr
console.log(__dirname); // Prints: /Users/mjr console.log(path.dirname(__filename)); // Prints: /Users/mjr 复制代码
我们在配置output是常用的几个
filename 这个是输出文件的名称,字符串类型,如果只有一个输出文件,可以写成静态名称。例如
output:{ filename:'bundle.js' } 复制代码
当然了,在我们日常工作中,一般情况下是不会有这种情况的,当项目很大的时候,如果不分块打包,bundle.js会惊人的大,项目越大,bundle.js就会越大,这不是我们今天讨论的重点,以后再说
多个chunk的时候怎么办呢?我们昨天说过,回忆一下
webpack会为每个生成的Chunk取一个名称,Chunk的名称和Entry的配置有关:
1. 如果entry是一个string或者array,就只会生成一个chunk,这个chunk的名称是main;
2. 如果entry是一个object,就可能出现多个chunk,这时chunk的名称是object键值对里键的名称
然而,当通过多个入口起点(entry point)、代码拆分(code splitting)或各种插件(plugin)创建多个
bundle,应该使用以下一种替换方式,来赋予每个 bundle 一个唯一的名称…… 使用入口名称:
output:{ filename: "[name].bundle.js" } 复制代码
使用内部 chunk id
output:{ filename: "[id].bundle.js" } 复制代码
使用每次构建过程中,唯一的 hash 生成
output:{ filename: "[name].[hash].bundle.js" } 复制代码
使用基于每个 chunk 内容的 hash:
output:{ filename: "[chunkhash].bundle.js" } 复制代码
这里多出来几个陌生词汇hash、chunkhash,它们是什么? hash、chunkhash和contenthash三者的区别 hash
hash的值是相同的,如果都使用hash的话,因为这是工程级别的,即每次修改任何一个文件,所有文件名的hash至都将改变。所以一旦修改了任何一个文件,整个项目的文件缓存都将失效。所以对于没有改变的模块而言,这样做显然不恰当,因为缓存失效了嘛。此时,chunkhash的用途随之而来。 chunkhash
只有被修改了的文件的文件名,hash值修改
filename: '[name]-[chunkhash].js'
当我们使用mini-css-extract-plugin拆分css的时候,就需要使用chunkhash,我一个js文件里面引入了css文件。这时要是我修改了js,但没修改css,可以通过chunkhash缓存css文件
contenthash
对css使用了chunkhash之后,我们测试会发现,如果修改了js,css文件名的hash值确实没变,但这时要是我们修改css文件的话,我们就会发现css文件名的chunkhash值居然没变化,这样就导致我们的非覆盖发布css文件失效了。所以这里需要注意就是css文件必须使用contenthash。
上面介绍的 id、name、hash、chunkhash等都是webpack内置变量, id是唯一标示,不会重复,从0开始, name 是模块名称,是你自己起的,在配置路由懒加载的时候可以自己命名 官网介绍的很清楚,我就不再这里啰嗦了, chunkFilename
官网解释:此选项决定了非入口(non-entry) chunk 文件的名称, 什么场景需要呢?
在按需加载(异步)模块的时候,也就是路由懒加载,这样的文件是没有被列在entry中的,
比如
{ entry: { "index": "pages/index.jsx" }, output: { filename: "[name].min.js", chunkFilename: "[name].min.js" } } const myModel = r => require.ensure([], () => r(require('./myVue.vue')), 'myModel') 复制代码
上面的例子,通过filename输出的是index.min.js 异步加载的模块是要以文件形式加载哦,所以这时生成的文件名是以chunkname配置的,通过chunkFilename输出的是myModel.min.js 所以chunkFilename也很重要哦!!! path path是配置输出文件存放在本地的目录,字符串类型,是绝对路径
output:{ path: path.resolve(__dirname, 'dist/assets') } 复制代码
__dirname,这个昨天说过,可以回顾一下,就是当前文件所在的文件夹的名字 publicPath 对构建出的资源进行异步加载(图片,文件),该选项的值是以 runtime(运行时) 或 loader(载入时) 所创建的每个 URL 为前缀。因此,在多数情况下,此选项的值都会以/结束。 默认值是一个空字符串 "",即相对路径,配置错误会导致404 简单说,就是静态文件托管在cdn上 举个栗子: 如果你这么配置:
output:{ filename:'[name]_[chunkhash:8].js', publicPath:'https://www.qdtalk.com/assets/' } 复制代码
打包编译后,html页面就是这样的
path 和publicPath都支持字符串模板
- 配置 单出口 // webpack 配置
const path = require('path'); module.exports = { entry: main: './src/main.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist') } }; 复制代码
- 配置 多出口
// webpack 配置 const path = require('path'); module.exports = { entry: { app: './src/app.js', vendors: './src/vendors.js' }, output: { filename: '[name].js', path: path.resolve(__dirname, 'dist') } }; 复制代码
3.modules(模块处理)
模块处理主要是对loader的配置
此处引用官网对loader的定义
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为webpack能够处理的有效模块,然后你就可以利用webpack的打包能力,对它们进行处理。
本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。 注意,loader 能够 import 导入任何类型的模块(例如 .css 文件),这是 webpack 特有的功能,其他打包程序或任务执行器的可能并不支持。我们认为这种语言扩展是有很必要的,因为这可以使开发人员创建出更准确的依赖关系图。 在更高层面,在 webpack 的配置中 loader 有两个目标:
1. test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。
2. use 属性,表示进行转换时,应该使用哪个 loader。
webpack.config.js const path = require('path'); const config = { output: { filename: 'my-first-webpack.bundle.js' }, module: { rules: [ { test: /\.txt$/, use: 'raw-loader' } ] } }; module.exports = config; 复制代码
以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性:test 和 use。这告诉 webpack 编译器(compiler) 如下信息:
“嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先使用 raw-loader 转换一下。” 重要的是要记得,在 webpack 配置中定义 loader 时,要定义在 module.rules 中,而不是 rules。然而,在定义错误时 webpack 会给出严重的警告。为了使你受益于此,如果没有按照正确方式去做,webpack 会“给出严重的警告”
在webpack中有许许多多的loader,此处我按照官方文档解释下和我理解的用法 loader 特性
- 几乎所有 loader 都 需要安装, 但 不需要 在 webpack 配置文件中通过 require 引入
- 逆向编译,链式传递 文件
- raw-loader 加载文件原始内容(utf-8)
- val-loader 将代码作为模块执行,并将 exports 转为 JS 代码
- url-loader 像 file loader 一样工作,但如果文件小于限制,可以返回 data URL
- file-loader 将文件发送到输出文件夹,并返回(相对)URL raw-loader 可以让你在代码中引入文件
import txt from './file.txt'; 复制代码
val-loader 加载的模块必须使用以下函数接口,将 default export 导出为一个函数。
function answer () { return { code: 'module.exports = 42;' } }; module.exports = answer; 复制代码
url-loader
url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。 file-loader 将文件发送到输出文件夹,并返回(相对)URL(不会再对文件做处理) JSON
- json-loader 加载 JSON 文件(默认包含)
- json5-loader 加载和转译 JSON 5 文件
- cson-loader 加载和转译 CSON 文件 主要处理json文件
转换编译(Transpiling)
- script-loader 在全局上下文中执行一次 JavaScript 文件(如在 script 标签),不需要解析
- babel-loader 加载 ES2015+ 代码,然后使用 Babel 转译为 ES5
- buble-loader 使用 Bublé 加载 ES2015+ 代码,并且将代码转译为 ES5
- traceur-loader 加载 ES2015+ 代码,然后使用 Traceur 转译为 ES5
- ts-loader 或 awesome-typescript-loader 像 JavaScript 一样加载 TypeScript 2.0+
- coffee-loader 像 JavaScript 一样加载 CoffeeScript
React开发过程中我们需要将jsx或者es6代码转译成es5代码。 我们需要用到babel-loader
{ 'test': /\.(js|jsx)$/, // babel 转换为兼容性的 js 'exclude': /node_modules/, 'loader': 'babel-loader', 'query': { 'presets': ['react', 'latest', 'stage-0', 'react-hmre'] }, 'include': path.resolve(__dirname, '../client') }, 复制代码
注:exclude是不转换node_modules的代码,query === options 此处注意babel版本6和7设置上有区别。 如果引用错误会报错 模板(Templating)
- html-loader 导出 HTML 为字符串,需要引用静态资源
- pug-loader 加载 Pug 模板并返回一个函数
- jade-loader 加载 Jade 模板并返回一个函数
- markdown-loader 将 Markdown 转译为 HTML
- react-markdown-loader 使用 markdown-parse parser(解析器) 将 Markdown 编译为 React 组件
- posthtml-loader 使用 PostHTML 加载并转换 HTML 文件
- handlebars-loader 将 Handlebars 转移为 HTML
- markup-inline-loader 将内联的 SVG/MathML 文件转换为 HTML。在应用于图标字体,或将 CSS 动画应用于 SVG 时非常有用。
样式
- style-loader 将模块的导出作为样式添加到 DOM 中
- css-loader 解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码
- less-loader 加载和转译 LESS 文件
- sass-loader 加载和转译 SASS/SCSS 文件
- postcss-loader 使用 PostCSS 加载和转译 CSS/SSS 文件
- stylus-loader 加载和转译 Stylus 文件 如果我们在开发中使用了less或者sass,我们需要先转换成css在引入到项目中
{ 'test': /\.less$/, 'loader': ['style-loader', 'css-loader', { 'loader': 'less-loader', 'options': { 'javascriptEnabled': true } }] }, 复制代码
此处的执行顺序。less-loader -> css-loader -> style-loader 清理和测试(Linting && Testing)
- mocha-loader 使用 mocha 测试(浏览器/NodeJS)
- eslint-loader PreLoader,使用 ESLint 清理代码
- jshint-loader PreLoader,使用 JSHint 清理代码
- jscs-loader PreLoader,使用 JSCS 检查代码样式
- coverjs-loader PreLoader,使用 CoverJS 确定测试覆盖率
框架(Frameworks)
- vue-loader 加载和转译 Vue 组件
- polymer-loader 使用选择预处理器(preprocessor)处理,并且 require() 类似一等模块(first-class)的 Web 组件
- angular2-template-loader 加载和转译 Angular 组件
核心重点(敲黑板)
plugins(插件)
作用:可以处理各种任务,从打包优化和压缩,一直到重新定义环境中的变量 loader不需要require. plugin需要
loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。
想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建它的一个实例。 下面是我配置的用于开发环境的plugins实例
webpack.config.js const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装 const webpack = require('webpack'); // 用于访问内置插件 const config = { module: { rules: [ { test: /\.txt$/, use: 'raw-loader' } ] }, plugins: [ new HtmlWebpackPlugin({template: './src/index.html'}) ] }; module.exports = config; 复制代码
'plugins': [ new webpack.optimize.OccurrenceOrderPlugin(), // 调整模块的打包顺序,用到次数更多的会出现在文件的前面 new webpack.DefinePlugin({ // DefinePlugin 允许创建一个在编译时可以配置的全局常量。 'process.env.NODE_ENV': JSON.stringify('development') }), new HtmlWebpackPlugin({ // HtmlWebpackPlugin简化了HTML文件的创建,以便为你的webpack包提供服务。这对于在文件名中包含每次会随着编译而发生变化哈希的 webpack bundle 尤其有用。 你可以让插件为你生成一个HTML文件,使用lodash模板提供你自己的模板,或使用你自己的loader。 'hash': true, 'title': 'Demo', 'filename': 'index.html', 'template': path.resolve(__dirname, '../views/index.ejs'), 'inject': 'body' }), new webpack.HotModuleReplacementPlugin(), // 启用热更新 new webpack.NoEmitOnErrorsPlugin(), // 输出阶段遇到编译错误跳过 new webpack.NamedModulesPlugin(), // 当开启 HMR 的时候使用该插件会显示模块的相对路径,建议用于开发环境。 new webpack.ProgressPlugin(), // 输出构建进度 ] 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- golang-新人入门配置学习
- AI 学习1:Anaconda 配置
- Istio 学习笔记(一):配置 GateWay
- Prometheus学习系列(十三)之配置解析
- DOCKER学习_005:Flannel网络配置
- Openstack Swift学习(三):log 配置
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。