My Little World

webpack应用

基本配置

1
2
3
4
5
6
7
8
const path = require('path');
module.export = {
entry:'./main.js',
output:{
filename:'bundle.js',
path:path.resolve(__dirname,'./dist')
}
}

接入babel

配置.babelrc

需要额外配置.babelrc文件,该文件是一个JSON格式文件
plugins属性,告诉Babel要使用哪些插件,这些插件可以控制如何转换代码
babel-plugin-transform-runtime:减少冗余代码
babel-runtime:可以导入babel-plugin-transform-runtime依赖的辅助函数
以上二者需要配套使用
presents属性,告诉Babel要转换的源码使用了那些新的语法特性,其值是一组plugins数组,
每一项,
如果不需要配置参数则使用字符串,表示插件名;
如果需要配置参数则使用数组,数组第一项为插件名,第二项为配置项组成的对象;
可以分为三大类:
1.已经被写入ES标准的特性:ES2015,ES2016,ES2017,Env (包含当前所有ES标准里的新特性)
2.社区提出但还未写入ES标准的特性:stage0,stage1,stage2,stage3,stage4,分别表示被纳入es标准的进度
3.特定场景下的语法特性,和es标准没有关系,例如要支持JSX,则使用babel-preset-react

1
2
3
4
{
"plugins":[["transform-runtime",{"polyfill":false}]]
"presents":[["es2015",{"modules":false}],"stage-4","react"]
}

webpack配置

Babel是转换代码功能,所以要配置相应loader

1
2
3
4
5
6
modules:{
rules:[{
test:/\.js$/,
use:['babel-loader']
}]
}

安装依赖模块

1
2
3
npm i -D babel-core babel-loader //webpack配置相关依赖
npm i -D babel-preset-env
npm i -D babel-preset-* //配置文件中的相关依赖

使用TypeScript

配置 tsconfig.json文件

1
2
3
4
5
6
7
8
9
"compilerOptions":{
"module":"commonjs",//编译的代码采用的模块规范
"target":"es5",//编译出的代码采用es哪个版本
"sourceMap":true,//输出Source Map 以方便调试
"importHelpers":true//禁止辅助函数重复出现在多个文件
},
"exclude":[
"node_modules"
]

webpack配置

使用loader做转换;修改文件查找规则

1
2
3
4
5
6
7
8
9
resolve:{
extension:['.ts','.js']
}
modules:{
rules:[{
test:/\.ts$/,
loader:'awesome-typescript-loader'
}]
}

安装依赖模块

1
npm i -D typescript awesome-typescript-loader

使用Flow检查器

使用语法

1
2
3
4
//@flow /*告诉检查器这个文件需要被检查*/
function squarel(n:number):number{
return n*n
}

使用配置

1.全局使用
npm i -g flow-bin 将可执行文件安装在全局,直接在项目根目录下使用flow命令执行代码检查
2.局部使用
npm i -D flow-bin 在项目中生成可执行文件,仅在某个项目中使用,
在package.json中配置使用命令

1
2
3
"script":{
"flow":"flow"
}

使用 npm run flow 执行代码检查

去除静态语法

采用flow静态语法的js无法在引擎中运行,所要去掉
1.使用flow-remove-types
2.一般使用flow的项目会使用es6,所以集成到babel配置中,
安装依赖模块:npm i -D babel-preset-flow
修改.babelrc,在preset中加入’flow’:”presets”:[…[],”flow”]

使用SCSS

SCSS又叫做SASS,可以使用一定语法编写css样式
转换方式
1.全局单文件转换
全局安装node-sass:npm i -g node-sass
执行命令:node-sass main.scss main.css //main.css即main.scss编译后的css文件

2.使用sass-loader

webpack配置

1
2
3
4
5
6
modules:{
rules:[{
test:/\.scss$/,
use:['style-loader','css-loader','sass-loader']
}]
}

安装依赖模块

npm i -D style-loader css-loader sass-loader //webpack loader
npm i -D node-sass //sass-loader 依赖node-sass

具体处理流程

sass-loader将scss源码转换为css,再将css代码交给css-loader
css-loader会找出css代码中@import 和 url()这样的导入语句,告诉webpack依赖这些资源,同时支持CSS modules,压缩CSS等功能
处理完后将结果给到style-loader
style-loader 会将CSS代码转换成字符串后,注入到js代码中,通过js向DOM增加样式,
如果想将css提取到单独文件中,可再使用ExtractTextPlugin插件提取

使用PostCSS

使用相应的loader 可以为css样式增加兼容浏览器的前缀,可以配置相应的配置文件,兼容下一代CSS语法

配置postcss.config.js文件

1
2
3
4
5
module.exports = {
plugins:[
require('postcss-cssnext') //postcss-cssnext支持下一代css语法编写
]
}

webpack配置

需要先将下一代语法转换成可识别css,同时添加前缀

1
2
3
4
5
6
modules:{
rules:[{
test:/\.scss$/,
use:['style-loader','css-loader','postcss-loader']
}]
}

安装依赖模块

npm i -D style-loader css-loader postcss-loader //webpack loader 依赖
npm i -D postcss-cssnext // postcss-loader 依赖

使用react

借助Bable

1.修改配置文件.babelrc,在preset中加入’react’:”presets”:[…[],”react”]
2.安装依赖模块
npm i -D react react-dom//react 基础依赖
npm i -D bable-preset-react //bable转换依赖

借助typescript

1.修改配置文件tsconfig.json

1
2
3
4
"compilerOptions":{
...,
"jsx":"react"//typescript本身支持jsx,开启JSX,支持react
},

2.修改webapck配置文件

1
2
3
4
5
6
7
8
9
resolve:{
extension:['.ts','.tsx',.js'] //typescript 原生支持jsx语法,只不过jsx语法文件后缀必须是tsx
}
modules:{
rules:[{
test:/\.tsx?$/,//同时匹配ts,tsx后缀文件
loader:'awesome-typescript-loader'
}]
}

3.安装依赖模块
npm i -D react react-dom//react 基础依赖
npm i -D @types/react @types/react-dom //react react-dom对应的ts接口描述模块,用于编译react,react-dom

使用VUE

引入

1.修改webapck配置文件

1
2
3
4
5
6
modules:{
rules:[{
test:/\.vue$/,
use:['vue-loader']
}]
}

2.安装依赖模块
npm i -S vue //vue框架依赖
npm i -D vue-loader css-loader vue-template-compiler //构建所需依赖
歌loader作用
vue-loader:解析转换.vue文件,提取其中的逻辑代码script样式代码style以及html模板template,然后交给对应的loader去处理
css-loader:加载vue-loader提取的CSS代码
vue-template-compiler:将vue-loader提取的HTML模板编译成对应可执行的js代码。

构建使用ts编写的vue

1.配置 tsconfig.json文件

1
2
3
4
5
6
"compilerOptions":{
"target":"es5",//构建es5版本的js,与VUE浏览器支持保持一致
"module":'es015',//使用es2015模块化格式
"strict":true,//开启严格模式,对this上数据属性进行严格推断
"moduleResolution":'node'
},

2.webpack配置

1
2
3
4
5
6
7
8
9
10
11
12
resolve:{
extension:['.ts','.js','.vue','json'] //增加对.ts,.vue文件支持
modules:{
rules:[{
test:/\.ts$/,//匹配ts后缀文件
loader:'ts-loader',
exclude:/node_modules/,
options:{
appendTsSuffixTo:[/\.vue$/] //让tsc将vue文件当成一个ts模块去处理,以解决module not found的问题,tsc本身不会处理.vue结尾文件
}
}]
}

3.新增文件vue-shims.d.ts
用以ts支持.vue结尾文件,可识别import 语句导入的.vue文件

1
2
3
4
declare module "*.vue"{
import Vue from "vue";
export default Vue
}

4.vue文件编写
script 部分

1
2
3
4
5
6
7
8
9
10
<script lang='ts'>//lang指明代码语法为ts
improt Vue from "vue"
export default Vue.extend({ //Vue.extend启用ts类型推断
data(){
return{
msg:'hello world'
}
}
})
</script

5.安装依赖
npm i -D ts-loader typescript

单页自动生成html

利用模板文件和插件生成

模板文件

模板文件用于描述哪些资源需要被以某种方式加入到输出的HTML文件中
资源链接URL字符串里问号前面的部分表示资源内容来自哪里,后面的参数表示这些资源注入的方式,可用&链接
_inline:资源是要引入动态资源
_dist:只在生产环境下才引入该资源
_dev:只在开发环境才引入该资源
_ie:只在IE浏览器中引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html lang="en">
<head>
<meta charset="utf-8">
<!--导入chunck app 中的css代码,可知要将css单独打包一个文件-->
<link rel="stylesheet" href="app?_inline">
<!--在这里引入/google_analytics的js代码-->
<script src="./google_analytics.js?_inline"></script>
</head>
<body>
<div id='app'></div>
<!--导入chunck app 中的js代码-->
<script type="text/javascript" src="app"></script>
</body>
</html>

webpack配置

使用web-webpack-plugin的webplugin自动生成index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const {WebPlugin} = require('web-webpack-plugin')
plugins:[
new WebPlugin({//自动生成index.html文件
template:'./template.html',//模板文件
filename:'index.html'//输出文件名
}),
new ExtractTextPlugin({
filename:`[name]_[contenthas:8].css` // 为输出的css文件名称加上Hash值
}),
new DefinePlugin({
'process.env':{//定义NODE_ENV环境变量为production,去除源码中只有开发时才需要的部分
NODE_ENV:JSON.stringify('production')
}
}),
new UglifyJsPlugin({//压缩输出的js代码
beautify:false,//最紧凑的输出
comments:false,//删除所有注释
compress:{
warnings:false,//删除没有用到的代码时,不发出警告
drop_console:true,//删除所有console语句,兼容IE
collapse_vars:true,//内嵌已定义但只用到了一次的变量
reduce_vars:true//提取出出现多次但没有定义成变量去引用的静态值
}
})

]

自动生成多个单页

需要解决的问题
1.要能够将公共代码提取出来,并能够注入到每个单页应用中
2.模板文件要支持注入公共文件和各个单页独自依赖的资源

模板文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html lang="en">
<head>
<meta charset="utf-8">
<!--在这里注入该页面所依赖但没有手动导入的CSS-->
<!--STYLE-->
<!--在这里引入/google_analytics的js代码-->
<script src="./google_analytics.js?_inline"></script>
</head>
<body>
<div id='app'></div>
<!-- 在这里注入该页面所依赖但没有手动导入的JS-->
<!--SCRIPT-->
</body>
</html>

此时模板文件被当作项目中所有单页的模板,因此不能直接写Chunck名称去引入资源,
因为需要被注入当前页面的Chunck名称不固定,每个单页都会有自己的名称
的作用在于保证该页面所依赖的资源都会被注入生成的HTML模板中
如果不存在,就注入到head标签最后
如果不存在,就注入到body标签最后

webpack配置

使用web-webpack-plugin的autoWebPlugin自动生成多个单页的index.html文件
但是对目录结构有要求,即所有单页各自的入口文件和依赖资源组成各自的一个文件夹,
多个文件夹放在同一目录A下,其他公共资源以及模板文件,webpack配置文件放在与A目录同级目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const {AutoWebPlugin} = require('web-webpack-plugin')
const autoWebPlugin = new AutoWebPlugin('pages',{ //pages 为各个单页的父目录A
template:'./template.html',//模板文件
postEntry:['./common.css'],//所有页面都依赖这份通用的CSS样式文件
commonChunk:{
name:'common',//提取公共代码chunk的名称
}
})
module.exports={
//AutoWebPlugin会为寻找到的所有单页生成对应的入口配置,
//autoWebPlugin.entry可以获取所有由autoWebPlugin生成的入口配置
entry:autoWebPlugin.entry({//可加入额外需要的Chunk入口}),
plugins:[autoWebPlugin]
}

构建基于react的同构应用

同构应用:写一份代码可同时在浏览器和服务器中运行的应用
能在服务器运行的原理核心是虚拟DOM
虚拟DOM好处:
1.操作DOM树是高耗时操作,可通过DOM diff算法找到两个不同Object的最小差异,得出最小的DOM操作
2.虚拟DOM在渲染时不仅可以通过操作DOM树表示结果,也可以有其他表示方式,例如将虚拟DOM渲染成字符串(服务器渲染)
或者渲染成手机APP原生UI组件(react Native)

服务器端构建配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const path = require('path')
const nodeExternals = require('webpack-node-externals')
module.exports = {
entry:'./main_server.js'
target:'node',//目标运行环境是node.js,源码依赖的nodejs原生模块没必要打包进去
externals:[nodeExternals()],//不打包node_modules中的第三方组件
output:{
libraryTarget:'commonjs2',//输出CommonJS2规范,供nodejs的http服务器代码调用
fillname:'bundle.server.js',
path:path(resolve(__dirname,'./dist'))
},
module:{
rules:[
{
test:/\.js$/,
use:['babel-loader'],
exclude:path.resolve(__dirname,'node_modules'),
},
{
test:/\.css/,
use:['ignore-loader'],//css文件不能打包到服务端代码,影响服务端渲染性能
}
]
}

}

文件准备

一个仅包含根组件代码,不能包含渲染入口代码,而且需要导出根组件以供渲染入口调用的文件 rootComponent.js

1
2
3
4
5
6
7
import react,{Component} from 'react';
import 'main.css'
export class RootComponent extends Component{
render(){
return <h1>hello world </h1>
}
}

不同环境渲染入口文件,一个环境一个
main_server.js

1
2
3
4
5
6
import react from 'react';
import RootComponent from './rootComponent'
import {renderToString} from 'react-dom/server' //计算表示虚拟DOM的HTML形式字符串
export function render(){
return renderToString(<RootComponent>)
}

main_browser.js

1
2
3
4
import react from 'react';
import RootComponent from './rootComponent'
import {render} from 'react-dom' //操作浏览器DOM树展示出结果
render(<RootComponent>,window.document.getElementById('app'))

http.server.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const express = require('express')
const {render } = require('./dist/bundle_server')
const app = express();
app.get('/',function(req.res){
res.send(`
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<div id='app'>${render()}</div>
<!--导入WEBPACK输出的用于浏览器端渲染的js文件-->
<script src='./dist/bundle_browser.js'></script>
</body>
</html>
`)
})
app.use(express.static('.'));
app.listen(3000,function(){
console.log('app is listening 3000')
})

执行命令

webpack –config webpac_server.config.js 得到./dist/bundle_server.js
webpack –config webpac.config.js 得到./dist/bundle_browser.js
node ./http_server.js 启动 http 服务器,访问’localhost:3000’看到服务器返回了html

基于react 的Electron应用

Electron 可以使用开发web应用的技术去开发跨平台的桌面应用,例如Atom,VSCode
是nodejs和Chromium浏览器的结合体,用Chromium浏览器显示出的WEB页面作为应用的GUI,通过Nodejs和操作系统交互
当操作一个Electron应用的一个窗口时,实际是在操作一个网页,当操作需要操作系统完成时,网页会通过Nodejs和操作系统交互
优点
1.降低了开发门槛,只需掌握网页开发技术和Nodejs,大量Web开发技术和现成库可以复用于Electron
而且由于Electron环境内置了浏览器和Nodejs的API,在开发网页时除了可以使用浏览器提供的API,还可以会用Nodejs的API
2.由于Chromium浏览器和Nodejs都是跨平台的,所有Electron能做到在不同操作系统运行一份代码

构建

Electron 应用每个窗口对应一个网页,所以相当于需要构建多单页面应用
在网页JS代码中可能会调用Nodejs原生模块或者Electron模块,输出的代码依赖这些模块但由于这些模块都是内置支持的,
所有构建出的代码不能将这些模块打包进去
由于webpack内置了对Electron的支持,只需要告诉webpack我要在electron环境里运行就可以实现

1
target:'electron-renderer'

运行

安装electron执行环境到项目中
npm i -D electron

在项目目录下执行electron ./就可以启动桌面应用

构建Npm模块

要求

1.源码若采用es6编写需要转换成es5,并且要遵守commonjs规范,同时提供Source Map方便调式

解决:
使用Babel将es6转换成es5
通过开启devtool:’source-map’输出Source Map以发布调试
设置output.libraryTarget = ‘commonjs2’ 实现输出代码符合CommomJS2规范
2.若为UI组件,则依赖的其他资源文件如css文件也需要包含的发布的模块中

解决:通过css-loader,extract-text-webpack-plugin实现,将css打包到单独文件

3.尽量减少代码冗余,(例如,Babel将es6转换成es5时,会注入一些辅助函数,例如实现class,extend语法的辅助函数,Babel会在每个输出文件中中内嵌依赖的辅助函数,多文件依赖的话,就会造成辅助函数重复出现,造成代码冗余)减少发布出去的组件的代码文件大小

解决:通过引入相同函数解决重复代码
使用babel-plugin-transform-runtime 将嵌入辅助函数代码转成引入辅助函数
引入bable-runtime模块用于提供辅助函数

4.发布出去的组件代码中不能含有其依赖的模块代码,例如react,babel-runtime,而是让用户可选择性的安装,否则可能在其他模块也依赖相同模块时,造成重复打包

解决:配置externals将外部环境提供的模块屏蔽掉,不进行打包

webpack配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')

module.exports = {
entry:'./src/index.js'
output:{
fillname:'index.js',
path:path.resolve(__dirname,'lib'),
libraryTarget:'commonjs2',
},
externals:/^(react|babel-runtime)/,
module:{
rules:[
{
test:/\.js$/,
use:['babel-loader'],
exclude:path.resolve(__dirname,'node_module'),
},
{
test:/\.css/,
use:ExtractTextPlugin.extract({
use:['css-loader']
})
}]
},
plugins:[
new ExtractTextPlugin({
fillname:'index.css'
})
],
devtool:'source-map'
}

.babellrc文件

1
2
3
4
5
6
7
8
9
10
{
'plugins':[
[
'transform-runtime',//默认自动注入ES6 API的polyfill
{
'polyfill':false//防止使用者在其他地方注入其他polyfill库,所以关闭注入polyfill功能
}
]
]
}

发布到Npm

修改package.json入口文件为打包后的文件

1
2
main:'lib/index.js',//webpack使用于构建不可分割的NPM模块,不能保持同源码结构一致例如如果打包lodash,会将所有工具函数打包进去,不适合仅用几个工具函数的场景
'jsnext:main':'src/index.js'//指出采用ES6编写的模块入口文件位置,便于实现Tree Sharking

离线应用 service workers打包

service workers了解

问题

1.如何生成sw.js文件
2.sw.js文件中的cacheFileList变量(代表被缓存文件的URL),需要根据输出文件列表所对应的URL来决定,不能写成静态值

解决

使用serviceworker-webpack-plugin,根据自定义sw.js生成含有输出文件列表的sw.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const {WebPlugin} = require('web-webpack-plugin')
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin')

module.exports = {
entry:{
app:'./main.js' //Chunk app 的js执行入口文件
},
output:{
fillname:'[name].js',
publicPath:'',
},
module:{
rules:[
{
test:/\.css/,//增加对CSS文件支持,
use:ExtractTextPlugin.extract({//提取到单独文件
use:['css-loader']//压缩CSS代码
})
}]
},
plugins:[
//一个WebPlugin对应一个HTML
new WebPlugin({
template:'./template/html',//HTML模板文件所在的文件路径
fillname:'index.html'//输出的HTML文件名称
}),
new ExtractTextPlugin({
fillname:'[name].css'
}),
new ServiceWorkerWebpackPlugin({
//自定义的sw.js文件所在路径
//ServiceWorkerWebpackPlugin会将文件列表注入生成的sw.js
entry:path.join(__dirname,'sw.js')
})
],
devServer:{
//Service workers依赖HTTPs,使用devServer提供https功能
https:true
}
}

构建出的sw.js文件会在头部注入一个变量serviceWorkerOption.assets到全局,里面存放着所有需要被缓存的文件的URL列表
因此需要将sw.js文件中写成静态值的cacheFileList替换成serviceWorkerOption.assets
var cacheFileList = global.serviceWorkerOption.assets

安装

npm -i -D web-webpack-plugin serviceworker-webpack-plugin

代码检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.exports = {
module:{
rules:[
{
test:/\.js$/,
exclude:/node_module/,//不检查node_module下文件
loader:'tslint-loader',//整合typeScript代码检查
enforce:'pre'//将执行顺序放到最前面,防止其他Loader将处理后的代码交给tslint-loader检查
},{
test:/\.js$/,
exclude:/node_module/,//不检查node_module下文件
loader:'eslint-loader',//整合eslint检查代码
enforce:'pre'//将执行顺序放到最前面,防止其他Loader将处理后的代码交给tslint-loader检查
}]
},
plugin:[
new StyleLintPlugin() //整合stylelint,检查css代码,可以解析SCSS,Less
]
}

导致问题

1.执行检查步骤计算量大,或导致webpack构建变慢
2.整合代码检查到webpack后,输出的错误信息是通过行号来定位错误的,没有编辑器集成显示错误直观

解决

1.将代码检查步骤当道代码提交时,即在代码提交前调用以上检查工具去检查代码,只有检查都通过时才提交代码,这样保证仓库内代码都经过检查
2.使用集成了代码检查功能的编辑器,让编辑器实时,直观的显示错误
安装 npm i -D husky 接入git hook,通过git 的hook功能做到在提交代码前触发执行,husky会通过Npm Script Hook自动配置好HOOK
但需要在package.json定义脚本,如下

1
2
3
4
5
6
7
{
"script":{
"precommit":"npm run lint",//git commit 前执行的脚本
"prepush":'lint',//git push前会执行的脚本
"lint":'eslint && stylelint'//调用eslint,stylelint检查工具
}
}

在nodejs中使用

通过调用Webpack的API,执行构建

一次构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const webpack = require('webpack')
//import webpack from 'webpack'

1.使用模块方式一
webpack({
//webpack配置,和webpack.config.js一样
},(err,stats)=>{
if(err || stats.hasErrors()){
//构建过程出错
}
//成功执行完构建
})
2.使用模块方式二
const config = require('./webpack.config.js')
webpack(config,callback)

启动监听模式

1
2
3
4
5
6
7
8
9
10
11
12
13
//不给webpack传递callback就会返回compiler实例,用于控制启动,而不是像上面那样立即启动
const compiler = webpack(config)
//调用compiler.watch并以监听模式启动,返回watching用于关闭监听
const watching = compiler.watch({
//watchOptions
aggregateTimeout:300
},(err,stats)=>{
//每次因文件发生变化而重新执行完/构建后
})
//调用watching.close关闭监听
watching.close(()=>{
//在监听关闭后
})

加载图片相关的loader

1.file-loader
将js和CSS中导入图片的地址替换成 webpack输出文件的地址,输出文件名是根据内容计算出的HASH值

1
2
3
4
rules:[{
test:/\.png$/,
use:['fill-loader']
}]

2.url-loder
将图片转base64直接注入到引入的地方,
一般利用url-loder将网页需要用到的小图片资源注入代码中,以减少加载次数,为一个很小图片而2新建一次HTTP连接不划算
如果图片体积太大会导致js,CSS文件过大而带来网页加载缓慢的问题

1
2
3
4
5
6
7
8
9
10
11
12
rules:[{
test:'/\./png$/',
use:[{
loader:'url-loader',
options:{
//30KB以下文件采用url-loader,控制文件大小
limit:1024*30,
//否则采用file-loader,默认值是file-loader
fallback:'file-loader'
}
}]
}]

还可以以下方式优化,同样适用于其他二进制类型的资源,如PDF,SWF
A.通过imagin-webpack-plugin压缩图片
B.通过webpack-spritesmith插件制作雪碧图

以上两个loader都可用于处理svg图片,但svg文件是文本格式文件,还有其他方法

3.raw-loader
可以将文本文件内容读取出来,注入js/CSS中
由于会直接返回svg的文本内容,并且无法通过CSS展示SVG的文本内容,因此采用该loader后无法在CSS中导入SVG

1
2
3
4
rules:[{
test:/\./svg$/,
use:'raw-loader'
}]

4.svg-inline-loader
类似raw-loader,但会分析SVG内容,去除其中不必要的部分代码,以减少SVG文件大小,相当于增加了对SVG的压缩能力

1
2
3
4
rules:[{
test:/\.svg$/,
use:['svg-inline-loader']
}]

DevServer 实现

webpack-dev-server本身基于webpack-dev-middleware和expressjs,而webpack-dev-middleware是一个express.js的中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const express = require('express')
const wepack = require('webpack')
const webpackMiddleware = require('webpack-dev-middleware')

const config = require('./webpack.config.js')
const app = expree()
const compiler = webpack(config)
app.use(webpackMiddleware(compiler,{
//在webpack-dev-middleware支持的所有配置项中
//只有publicPath属性为必填项,其他都是选填项

//webpack输出资源绑定HTTP服务器上的根目录
//同WEBPACK配置中的publicPath
publicPath:'/assets/',

//不输出info类型的日志到控制台,只输出warn和error类型的日志
noInfo:false,
//不输出任何类型的日志到控制台
quiet:false,
//切换到懒惰模式,意味着不监听文件的变化,只会在有请求时再编译对应的文件,适合页面很多的项目
lazy:true,
//watchOptions,只在非懒惰模式下才有效
watchOptions:{
aggregationTimeout:300,
poll:true
},

//默认的URL路径,默认是'index.html'
index:'index.html',
//自定义HTTP头
headers:{'X-Custom-Header':'yes'},
//为特定后缀的文件添加HTTP mimeTypes,作为文件类型映射表
mimeTypes:{'text/html':['phtml']},

//统计信息输出样式
stats:{
colors:true
},
//自定义输出日志的展示方法
reporter:null,
//开启或关闭服务端渲染
serverSideRender:false

}))
//webpackMiddleware函数返回一个Expressjs中间件,该中间件有俩个功能
//1.接收来自webpack compiler实例输出的文件,但不会将文件输出到硬盘中,而会保存在内存中
//2.在express.js上注册路由,拦截HTTP收到的请求,根据请求路径响应对应文件内容

//webpack-dev-middleware没有模块热替换功能,但Devserver有,
//可通过webpack-hot-middleware中间件来支持模块热替换,响应用于替换老模块的资源
app.use(require('webpack-hot-middleware')(compiler))
//将项目根目录作为静态资源目录,用于服务器HTML文件
app.use(express.static('.'))

app.listen(3000)

响应模块热替换功能还需要作如下配置
1.修改webpack.config.js文件,加入HotModuleReplacementPlugin插件
相当于执行 webpack-dev-server –hot工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const HotModuleReplacementPlugin= require('webpack/lib/HotModuleReplacementPlugin')
module.exports={
entry:[
'webpack-hot-middleware/client',//为了支持模块热替换注入代理客户端
'./src/main.js'
],
output:{
filename:'bundle.js'
},
plugin:[
//为了支持模块热替换,生成.hot-update.json文件
new HotModuleReplacementPlugin(),
]
}

2.修改入口文件main.js,加入替换逻辑

1
2
3
4
//在文件末尾加入
if(module.hot){
moudule.hot.accept()
}

3.安装以上配置中用到的依赖
npm i -D webpack-dev-middleware webpack-hot-middleware express