前言

webpack前端工程中扮演的角色越来越重要,它也是前端工程化很重要的一环。本文将和大家一起按照项目流程学习使用wbepack,妈妈再也不用担心我不会使用webpack,哪里不会看哪里。这是一个由浅入深的文章。

工程化

这里是一个项目工程化,规范化的设置,如果是初次使用webpack的同学还是最后在看这一块知识

现在vue、react等脚手架都会自动将开发环境使用的webpack的配置文件和生产环境的配置文件分开,将压缩代码,添加hash控制版本等操作放在项目上线时运行,这样避免了在开发阶段打包时间过长的问题。比如像这样,把两个环境的配置文件分开。


下面来看下两个配置文件的内容(我是用的typescript开发react,内容请忽略)
开发环境:

生产环境:


可以看到,开发环境增加了几个插件,这样做的好处就是更加工程化,规范化,降低开发环境的打包时间,代码维护性也更高。

分开写配置文件就要涉及到使用命令执行不同的配置文件,我们可以使用npm的脚本命令,我们可以在package.json中找到scripts,添加如下命令"build": "NODE_ENV=production webpack --config ./webpack.production.config.js --progress"

给大家解释下这个命令的意思

  • NODE_ENV=production 就是将运行环境设置成生产环境
  • webpack --config 就是运行webpack的配置文件
  • ./webpack.production.config.js 是要运行的指定位置的文件,这个路径是相对根目录来说的
  • --progress 是编译过程显示进程百分比的

如果你不追求规范化和工程化,我们就写一个配置文件就好,这里没有硬性要求。下面我们来讲webpack的具体配置

开始

在我们对于webpack不是特别熟练的时候,我们可能不会写全配置文件,往往是用到什么再去添加,下面我们就按照这个步骤彻底学会使用webpack。

module.exports = {
entry: './src/index.js' // 这里是项目入口文件地址
ouput: {
path: __dirname + "/dist", // 这里是项目输出的路径,__dirname表示当前文件的位置
filename: "js/"+"[name].js" // 这里是生成文件的名称,可起你想要的名字
}
}

loader

这就是我们最初一个骨架,下面我们再添加一些配置,比如你使用的是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" }
]
},
}

css/css预处理语言(less、sass、stylus)

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即可。

  • style-loader 将css插入到页面的style标签
  • css-loader 是处理css文件中的url()等
  • less-loader 是将less文件编译成css

postcss解决css兼容问题

写到这里我们会突然想到一个点,就是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图片的使用

我们在开发时,往往会遇到一些图标图片在不同情况下会失真,以及资源过多,我们需要减小图标类图片的大小,这时我们就需要引入svg,国内可能都会去使用阿里的iconfont库,从而引入svg图标,解决上面的问题

我们打开下载的素材文件夹,发现里面有一些.woff、.svg、.eot的文件,我们要想使用svg的图标还必须依赖这些文件,这时webpack不支持这些文件,我们需要引入新的loader

{ test: /\.(woff|svg|eot|ttf)?$/, loader: "url-loader" }

下面我们就能愉快的使用svg图标了,不存在失真的情况,同时会很小

webpack-dev-server

写到这,我们可能不断的打包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代码

我们打包完成的项目往往比较大,包含很多空格,占用了很大空间,这时我们要通过压缩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文件单独拆分出来,这样的好处就是打包的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,谢谢大家

学习webpack的更多相关文章

  1. [译]开始学习webpack

    写在前面: 文章翻译自petehunt大神的petehunt/webpack-howto,作为学习webpack的开始.fork了一份,翻译后的在这里https://github.com/zjzhom ...

  2. 深入学习webpack(一)

    深入学习webpack(一) 模块化的相关库和工具已经很多了,包括require.js.sea.js和一些工程化工具webpack.gulp.grant.那么我们该如何选择呢? 其实,我们只需要掌握了 ...

  3. webpack4 学习 --- webpack和webpack-dev-server

    以前了解过webpack2, 所以对webpack 不是很陌生,就直接入主题吧.新建一个文件夹,就叫它webpack-tut吧.然后在文件中新建一个src 文件夹,存放我们的源文件,再在src 文件夹 ...

  4. 如何学习 Webpack

    webpack-howto Tip: 本文是 webpack-howto 的原文,我觉得这篇文章写得非常好,确实算是目前学习 webpack 入门的必读文章.直接收录之. 本教程的目标 这是一本教你如 ...

  5. 深入学习webpack(二)

    深入学习webpack(二) 在深入学习webpack(一)中,我通过一个例子介绍了webpack的基本使用方法,下面将更为系统的学习webpack的基本概念,对于一门技术的掌握我认为系统化还是很重要 ...

  6. 跟我一起学习webpack使用配置文件(二)

    接着跟我一起学习webpack(一)中的项目来,我们接下来使用配置文件 使用npx webpack -h 我们可以查看webpack的配置参数 从我们在package.json中添加的命令来看,当项目 ...

  7. 学习webpack基础笔记01

    学习webpack基础笔记 1.webpack搭建环境最重要的就是如何使用loader和plugins,使用yarn/npm安装插件.预处理器,正确的配置好去使用 2.从0配置webpack - 1. ...

  8. vue第二单元(webpack的配置-学习webpack的常用配置)

    第二单元(webpack的配置-学习webpack的常用配置) #课程目标 掌握webpack的常用配置 掌握如何根据实际的需求修改webpack的对应配置 了解webpack-dev-server的 ...

  9. 零基础学习webpack打包管理

    这些天在项目之余的时间学习了webpack打包项目的东西,非常荣幸的找到一些大神的文章来学习,死劲嚼了几天,终于略知一二.在以后的工作上还需继续学习,下面我将分享我这几天学到的一点东西,希望能让我一个 ...

  10. Webpack学习-Webpack初识

    一.前言 webpack 到底是个什么东西呢,看了一大堆的文档,没一个能看懂的,因为上来就是给个module.exports 然后列一大堆配置,这个干啥,那个干啥,没一点用.但凡要用一个东西,一个东西 ...

随机推荐

  1. Spring Boot 学习笔记 - 01

    看了[纯洁的微笑]的博客后,我决定开始学好 Spring 体系了,真的是解决了饥渴的我.

  2. LoadRunner脚本增强技巧之手动关联

    个人认为LoadRunner的手动关联技巧可以看做参数化的一种,区别在与获取参数的来源不同. 一般的参数化:把脚本中一个写死的数值或者字符串用一个变量参数来代替,每次迭代的时候,从本地文件或者数据库中 ...

  3. Matrix Walk CodeForces - 954C

    题意: 就是给出一连串的数字 这些数字是从第一个数字依次走过的 emm..就是这样..  然后让你判断这个矩阵是否存在  如果存在输出行和列的数量  其中行..开到最大就好了...主要是判断列 在输入 ...

  4. 监听input内容改变的oninput与onpropertychange在ie9的bug

    在做autocomplate的时候发现,ie9中,剪切.退格.删除不触发oninput事件,而ie9和ie9+已经移除了onpropertychange事件. 只好尝试添加退格.delete.剪切事件 ...

  5. 【bzoj4591】超能粒子炮·改

    Portal-->bzoj4591 Solution 首先这个模数是一个质数 然后看一下那个\(k\)和\(n\)的范围..行吧Lucas定理咯 但是如果直接求: \[ \sum\limits_ ...

  6. 新的JavaScript数据结构Streams

    最近在网上看到了一个新的 Javascript 小程序——Streams,起初以为是一个普通的 Javascript 类库,但读了关于它的介绍后,我发现,这不是一个简单的类库,而且作者的重点也不是这个 ...

  7. mysql group_concat方法用法

    目前有三张表: 课程表: course,里面有student_id 学生表:     student,里面有 name 字段 中间表(课程和学生一对多): course_student, 里面有 co ...

  8. Codeforces Round #546 (Div. 2) ABCDE 题解

    1136A: 题意:一本书有n个章节,每个章节的分别在li到ri页,小明读完书后将书折在第k页,问还有多少章节没有读 题解:控制k在li~ri的范围内后输出n-i即可 #include <set ...

  9. Prefetch count--预取数量

    一.前言 前面提到如果有多个消费者同时订阅同一个Queue中的消息,Queue中的消息会被平摊给多个消费者.这时如果每个消息的处理时间不同,就有可能会导致某些消费者一直在忙,而另外一些消费者很快就处理 ...

  10. 「Python实践」学习之路

    一.列表内容对比 方式一: import operatorl1 = ['a','b','c']l2 = ['a','d','e']print(operator.lt(l1,l2))运行结果为True ...