webpack前端工程中扮演的角色愈来愈重要,它也是前端工程化很重要的一环。本文将和你们一块儿按照项目流程学习使用wbepack,妈妈不再用担忧我不会使用webpack,哪里不会看哪里。这是一个由浅入深的文章。javascript
这里是一个项目工程化,规范化的设置,若是是初次使用webpack的同窗仍是最后在看这一块知识css
如今vue、react等脚手架都会自动将开发环境使用的webpack的配置文件和生产环境的配置文件分开,将压缩代码,添加hash控制版本等操做放在项目上线时运行,这样避免了在开发阶段打包时间过长的问题。好比像这样,把两个环境的配置文件分开。html
下面来看下两个配置文件的内容(我是用的typescript开发react,内容请忽略)
开发环境:前端
生产环境:vue
能够看到,开发环境增长了几个插件,这样作的好处就是更加工程化,规范化,下降开发环境的打包时间,代码维护性也更高。java
分开写配置文件就要涉及到使用命令执行不一样的配置文件,咱们可使用npm的脚本命令,咱们能够在package.json中找到scripts,添加以下命令"build": "NODE_ENV=production webpack --config ./webpack.production.config.js --progress"node
给你们解释下这个命令的意思react
若是你不追求规范化和工程化,咱们就写一个配置文件就好,这里没有硬性要求。下面咱们来说webpack的具体配置webpack
在咱们对于webpack不是特别熟练的时候,咱们可能不会写全配置文件,每每是用到什么再去添加,下面咱们就按照这个步骤完全学会使用webpack。web
module.exports = {
entry: './src/index.js' // 这里是项目入口文件地址 ouput: { path: __dirname + "/dist", // 这里是项目输出的路径,__dirname表示当前文件的位置 filename: "js/"+"[name].js" // 这里是生成文件的名称,可起你想要的名字 } }
这就是咱们最初一个骨架,下面咱们再添加一些配置,好比你使用的是react,那么你就须要添加react的相关loader,这里以typescript编写的react为例。
module.exports = {
entry: './src/index.js' // 这里是项目入口文件地址 ouput: { path: __dirname + "/dist", // 这里是项目输出的路径,__dirname表示当前文件的位置 filename: "js/"+"[name].js" // 这里是生成文件的名称,可起你想要的名字 }, module: { rules: [ { test: /\.tsx?$/, loader: "awesome-typescript-loader" }, { enforce: "pre", test: /\.js$/, loader: "source-map-loader" } ] }, }
webpack是将一个个文件分拆成一个个模块(module)来进行编译打包的,咱们全部的处理文件内容的东西都要放在module里,rules即规则。
rules里面的两个loader都是编译.tsx文件及处理错误信息的。
在你写好了组件以后,你须要开始编写样式,但不管是css仍是使用less、sass等预处理语言,webpack都是没法直接处理的,咱们安装并使用相应的loader。下面以less和css为例。
{test: /\.(less|css)?$/, loader: ["style-loader", "css-loader", "less-loader"]}
webpack会按照从右到左的顺序执行loader,咱们新解析less,以后进行css的打包编译。若是你不适用less等预处理语言,安装css-loader和style-loader便可。
写到这里咱们会忽然想到一个点,就是css样式的兼容性问题,靠人工去写的话,你内心可能会有一句mmp不值当讲不当讲,哈哈,咱们必须使用postcss来解决这个问题。
postcss是目前css兼容性的解决方案,会自动帮咱们加入前缀,以使css样式在不一样的浏览器能兼容,这里安装使用postcss-loader
{ test: /\.(less|css)?$/, loader: ["style-loader", "css-loader", "less-loader", "postcss-loader"]}
postcss-loader要写在最后(其实只要放在css-loader以后就能够),写到这你觉得就能够了吗?只能说 too young,postcss解决兼容性问题主要靠的实际上是它的插件autoprefixer,咱们还须要在根目录写一个postcss.config.js的配置文件,以下
module.exports = {
plugins: [
require('autoprefixer') ] };
写到这,咱们就不用再担忧css兼容性问题了,就想使用babel文件同样,这个文件会自动解析,咱们不须要管它。
咱们在开发时,每每会遇到一些图标图片在不一样状况下会失真,以及资源过多,咱们须要减少图标类图片的大小,这时咱们就须要引入svg,国内可能都会去使用阿里的iconfont库,从而引入svg图标,解决上面的问题
咱们打开下载的素材文件夹,发现里面有一些.woff、.svg、.eot的文件,咱们要想使用svg的图标还必须依赖这些文件,这时webpack不支持这些文件,咱们须要引入新的loader
{ test: /\.(woff|svg|eot|ttf)?$/, loader: "url-loader" }
下面咱们就能愉快的使用svg图标了,不存在失真的状况,同时会很小
写到这,咱们可能不断的打包webpack了,太麻烦了,因而乎webpack-dev-server应运而生。它是webpack提供的服务器,咱们使用npm i webpack-dev-server --save-dev来安装。
咱们其实在命令行中敲击webpack-dev-server --open就能够开启,默认是localhost:8080开启,如今咱们不须要在重复使用webpack命令打包,安装。
值得注意的是,webpack-dev-server打包的文件会存在内存中,因此在index.html中引入文件的时候就要以下,这里是默认输出文件是bundle.js
<script src="bundle.js"></script>
今天咱们不重点讲webpack-dev-server,之后我会再写文章,深刻的讲解其使用。
可能咱们在开发阶段只用到了这几个功能,下面咱们来说一下项目上线的准备。
咱们打包完成的项目每每比较大,包含不少空格,占用了很大空间,这时咱们要经过压缩js来减少文件体积。webpack自带了UglifyJsPlugin插件来压缩js代码,使用以下
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false } }) ]
咱们的插件统一放在export.modules = {}的plugins里面,它是一个数组,使用插件时new 一个实例便可。这里咱们使用到webpack实例,因此要在配置文件头部引入webpack,即var webpack = require('webpack');
咱们在使用的js库如vue或者react等的时候,webpack会将它们一块儿打包,react和react-dom文件就好几百k,所有打包成一个文件,可想而知,这个文件会很大,用户在首次打开时就每每会出现白屏等待时间过长的问题,这时,咱们就须要将这类文件抽离出来。
externals: {
"react": "React", "react-dom": "ReactDOM" },
这里咱们会用到externals,它和plugins是平级。左侧key表示依赖,右侧value表示文件中使用的对象。好比在react开发中,咱们经常会这样在文件头部写import React from 'react',这里你们能够和上面对号入座下。
这里咱们就须要对这个文件进行单独的引入使用了,在index.html中添加以下代码
<script src="./node_modules/react/umd/react.xxx.js"></script> <script src="./node_modules/react-dom/umd/react-dom.xxx.js"></script>
写到这,咱们就已经将文件拆分了。
不过,咱们在项目上线的时候不可能会带有node_modules,因此咱们就须要使用一个copy插件将react和react-dom文件复制出来
new CopyWebpackPlugin([ // from是要copy的文件,to是生成出来的文件
{ from: "node_modules/react/umd/react.xxx.js", to: "js/react.min.js" }, { from: "node_modules/react-dom/umd/react-dom.xxx.js", to: "js/react-dom.min.js" } { from: "public/favicon.ico", to: "favicon.ico" } ])
这样咱们的index.html文件中就要写成下面这种形式
咱们也能够将css文件单独拆分出来,这样的好处就是打包的css文件咱们能够放到cdn上,而后缓存到浏览器客户端中。这样就尽量的减少文件的体积,以及没必要要的资源从新加载,浪费带宽。
咱们要先安装插件
npm install extract-text-webpack-plugin --save-dev
配置文件添加对应配置
var ExtractTextPlugin = require("extract-text-webpack-plugin");
plugins里面添加插件
new ExtractTextPlugin("styles.css")
下面是具体的使用
module.exports = {
// entry和output自动省略
module: {
loaders: [{
test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader!postcss-loader') // 这里我目前使用less尚未成功 }] }, postcss: function() { return [autoprefixer, cssnext, precss, cssnano] }, plugins: [ new ExtractTextPlugin('./css/[name].min.css') // 生成到css文件夹下 ] }
webpack会将全部引用到的css文件打包,最终生成./css/[name].min.css文件。
这里对图片进行base64进行转码一样是减少资源的体积
安装 url-loader
npm install url-loader --save-dev
在modules的rules里面添加
{
test: /\.(png|jpg)$/, loader: 'url?limit=8192&name=images/[hash:8].[name].[ext]' }
limit 设置一个阈值,小于这个值得图片就会自动启用 base64 编码的图片,大于这个值的图片会打包到name 这参数对应的路径,图片名称就会包括8位md5编码 name 对应文件原本名称,ext 对应扩展名
咱们的后台会给资源设置Cache-Control: max-age=秒替代,来对资源进行缓存时间的设置,这使得咱们在刷新页面以后会去缓存中加载资源,可是存在一个问题,就是,一旦咱们更新版本以后,客户没有去清除缓存,同时缓存尚未过时的状况下,就没法加载到最新的资源。这时咱们就须要hash值来进行版本控制
咱们一般这样作
output: {
path: __dirname + "/dist", filename: "[name][hash].js" }
给输出文件加上[hash]来添加hash值,这样就能够作到用户加载html里会去加载对应hash值得打包文件,好比下面这样
<script type="text/javascript" src="main3d1cb903f77dad5737e9.js"></script>
打包出来的js文件是这样
这样就能解决这个问题了。
咱们不可能每次都去手动复制一个index.html到打包好的dist文件中,咱们会使用一款插件html-webpack-plugin
它能够自动添加html文件到dist文件中,同时它会自动添加js文件并带有hash值
引入插件
var HtmlWebpackPlugin = require('html-webpack-plugin');
使用插件
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.tmpl.html'), filename: 'index.html' })
这里给你们解释下,template是模板,咱们在不少状况下,生产环境和开发环境不一样,致使index.html引入的资源路径不一样,这是为了改来改去,咱们能够建立一个模板,它指定编译时咱们copy的index.html文件。filename是最终生成的文件名。
模板文件以下
<!DOCTYPE html>
<html lang="zh"> <head> <meta charset="UTF-8"> <link rel="icon" href="favicon.ico"> <title>Projection-Web</title> </head> <body> <div id="root"></div> <script src="js/react.min.js"></script> <script src="js/react-dom.min.js"></script> </body> </html>
生成的index.html文件以下
<!DOCTYPE html>
<html lang="zh"> <head> <meta charset="UTF-8"> <link rel="icon" href="favicon.ico"> <title>Projection-Web</title> </head> <body> <div id="root"></div> <script src="js/react.min.js"></script> <script src="js/react-dom.min.js"></script> <script type="text/javascript" src="js/main3d1cb903f77dad5737e9.js"></script></body> </html>
下面是我打包编译的dist文件夹内容
下面是生产环境的配置文件(部分)
var CopyWebpackPlugin = require("copy-webpack-plugin"); var HtmlWebpackPlugin = require('html-webpack-plugin'); var webpack = require("webpack"); var path = require('path'); var ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { entry: "./src/index.tsx", output: { path: __dirname + "/dist", filename: "js/"+"[name][hash].js" }, devtool: "source-map", resolve: { extensions: [".ts", ".tsx", ".js", ".json"] }, module: { rules: [ { test: /\.tsx?$/, loader: "awesome-typescript-loader" }, { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }, { test: /\.(less|css)?$/, loader: ["style-loader", "css-loader", "less-loader", "postcss-loader"] }, { test: /\.(woff|svg|eot|ttf)?$/, loader: "url-loader" } ] }, externals: { "react": "React", "react-dom": "ReactDOM" }, plugins: [ new CopyWebpackPlugin([ { from: "node_modules/react/dist/react.js", to: "js/react.min.js" }, { from: "node_modules/react-dom/dist/react-dom.js", to: "js/react-dom.min.js" }, { from: "index.html", to: "index.html" }, { from: "public/favicon.ico", to: "favicon.ico" } ]), new HtmlWebpackPlugin({ template: path.join(__dirname, 'src/index.tmpl.html'), filename: 'index.html' }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] };
学好webpack,是一名现代前端开发工程师的基本素养。后续还会深刻webpack,谢谢你们