内容简介:自己配置过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(), // 输出构建进度
]
复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
High Performance Python
Micha Gorelick、Ian Ozsvald / O'Reilly Media / 2014-9-10 / USD 39.99
If you're an experienced Python programmer, High Performance Python will guide you through the various routes of code optimization. You'll learn how to use smarter algorithms and leverage peripheral t......一起来看看 《High Performance Python》 这本书的介绍吧!