webpack 基础
Wepack 工程打包机
Webpack 基本概念
Webpack Demos
Node.js Debugger
webpack学习实践系列
Webpack Dev Server
Webpack loader 十问
Webpack 总得来说是一个资源模块化 JS Module 打包工具,它的核心思想是模块化思想,不管你是图片,JS,CSS,SCSS,LESS,还是 JSX,统统都打包成 JS Module,Anythin to JS Modules。各种资源都有相适配的加载器 Loaders 负责对资源进行处理,Webpack 通过执行加载器完成原始资源的转换。
Hello Webpack
TypeScript
TypeScript Samples
TypeScript Node Starte
TS-Node
Webpack with TypeScript
Awesome TypeScript Loader
TypeScript loader for webpack
Babel loader for webpack
Base64 URL Loader
Webpack 资源管理
TypeScript声明文件 .d.ts
Build Performance
这里同过 TypeScript + Webpack 编写一个 Demo 工程来入门 Webpack 工程化打包机的理念,工程化是必然的软件技术发展方向。TypeScript 作为一个强大的静态类型检查语言是作为工程化开发的理想工具,所以 Node.js + TypeScript + Webpack + 框架的工程结构将会是非常流行的技术栈。TypeScript 现在发布 v3.5.3 版本,官方提供的示例 [TypeScript Node Starte] 是非常值得学习的案例。
Node.js 命令行的 TypeScript 编译器可以使用 npm 来安装,安装后会有一个 tsc 命令来转译 TypeScript 代码为 JavaScript,也可以安装 ts-node 来直接解析运行。
npm install --save-dev [email protected] [email protected]
tsc helloworld.ts
ts-node helloworld.ts
使用 VSCode、Sublime Text、Vim 作为开发工具都是很好的选择。
通过 Node.js 环境使用 Webpack,先通过 npm 来进行全局安装,创建示例工程 webpack-demo
目录,npm init
项目初始化命令生成默认的 package.json
配置文件,可以传入默许参数 --yes
忽略设置内容,关于它得用法可以查询 npm help init
。一并安装 webpack-dev-server
这个开发用的 Web 服务器,还有 TypeScript 支持。
Webpack 处理 TypeScript (.ts
) 原代码文件需要用到 ts-loader
或 awesome-typescript-loader
,前者使用得更多。在代码中导入文件资源时需要 file-loader
,导入图片时需要 url-loader
,它可以对常用得图片进行 Base64编码,可以设置 limit
选项来限定待编码文件大小。在处理 CSS 样式文件时会用到 css-loader
,打包后的样式在页面上还原出来时需要style-loader
提供的功能,它会在 节点下插入一个
节点,通过写入样式规则来还原经过打包的样式。
由于 TypeScript 静态类型的特殊性,那些非代码资源需要为 TypeScript 提供一个 Type Declaration 类型声明文件 .d.ts
,在工程原代码目录下保存即可,TypeScript 会自动解析。如编写一个 .svg
资源的D类型声明文件:
// custom.d.ts
declare module "*.svg" {
const content: any;
export default content;
}
这个 SVG 类型声明模块指明,任何以 .svg
结尾的文件导入时将拥有一个 any 任意类型属性的 content,即数据部分是任意类型。还可以显式定义 url
属性为 string,即文件的地址。 这个De类型声明规则同样适用于 CSS, SCSS, JSON 等等,这是 TypeScript 静态类型系统特有的做法。
TypeScript 相比 JavaScript 增加了类型声明。这些类型声明帮助编译器识别类型,从而防止开发者搬起石头砸自己的脚。
原则上,TypeScript 需要开发者做到先声明后使用。这就导致开发者在调用很多原生接口(浏览器、Node.js)或者第三方模块的时候,因为某些全局变量或者对象的方法并没有声明过,导致编译器的类型检查失败。
用 ts 写的模块在发布的时候仍然是用 js 发布,这就导致一个问题:ts 那么多类型数据都没了,所以需要一个 d.ts 文件来标记某个 js 库里面对象的类型
然后 typings 就是一个网络上的 .d.ts
数据库。
Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。它通过降低 array、number、objects、string 等等的使用难度从而让 JavaScript 变得更简单的工具类。Lodash 的模块化方法 非常适用于做 array、object 和 string 的遍历操作,对值进行操作和检测,创建符合功能的函数等。
source-map-loader
是一个 SourceMap 代码地图工具,可以生成代码地图方便做调式。配置时可以在配置 devtool
设置内联 inline-source-map
或 source-map
方式,前者会将调式代码嵌入打包输出,后者则独立保存为 .map
代码地图文件。
mkdir webpack-demo
cd webpack-demo
npm init --yes
npm install --save-dev webpack@4.1.1 webpack-cli@3.3.6 webpack-dev-server@3.7.2
npm install --save-dev file-loader@4.0.0 url-loader@2.0.1 ts-loader@6.0.4 css-loader@3.0.0 style-loader@0.23.1
npm install --save-dev awesome-typescript-loader@5.2.1
npm install --save-dev lodash@4.17.14 source-map-loader@0.2.4
配置文件参考,main
这里设置的式项目入口程序,如果已经准备好配置文件,直接执行 npm install
就可以根据配置好的依赖模块列表进行下载安装:
{
"name": "webpack",
"version": "1.0.0",
"description": "Webpack Getting Started",
"main": "index.ts",
"devDependencies": {
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.0.0",
"file-loader": "^4.0.0",
"html-webpack-plugin": "^3.2.0",
"html-webpack-template": "^6.2.0",
"lodash": "^4.17.14",
"source-map-loader": "^0.2.4",
"style-loader": "^0.23.1",
"ts-loader": "^6.0.4",
"ts-node": "^8.3.0",
"typescript": "^3.5.3",
"url-loader": "^2.0.1",
"webpack": "^4.1.1",
"webpack-cli": "^3.3.6",
"webpack-dev-server": "^3.7.2"
},
"dependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --devtool eval --progress --colors",
"build": "set NODE_ENV=production&& webpack -p"
},
"author": "Jeango",
"license": "ISC"
}
需要注意的式,加载的模块愈多,就越消耗硬件资源,因此没有必要使用的组件就不要安装了,直接从配置文件中移除然后从新执行 npm install
安装即可更新配置。
例如给给规则指定 include
目录节省搜索时间,简化 resolve.modules
, resolve.extensions
, resolve.mainFiles
, resolve.descriptionFiles
。使用 DllPlugin
插件将那些不怎么修改的内容分离到另一个编译单元,避免在开发过程使用 source-map
或 minimize
,特别是代码地图 Source maps 它非常耗资源,请考虑是否真的需要。
可以使用并行编译,parallel-webpack
和 cache-loader
提供并行编译的能力,后者对性能开销较大的 loader 提供缓存服务。具体要点参考官方的构建优化文档[Build Performance]。
rules: [
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader'
}
]
webpack 命令的基本用法
webpack – building for development
webpack -p – building for production (minification)
webpack --watch – for continuous incremental building
webpack -d – including source maps
webpack --colors – making building output pretty
为了方便使用打包命令,可以配置到 package.json
中的 scripts
,例如要运行开发服务器 webpack-dev-server
只需要执行 npm run dev
,要生成发布打包就执行 npm run build
,另外 VSCode 对这里设置的命令配置项支持很到位,直接通过 VSCode 的 Terminal 菜单 Run Task
就可以执行。Sublime
{
"scripts": {
"dev": "webpack-dev-server --devtool eval --progress --colors",
"build": "NODE_ENV=production webpack -p"
},
}
接下来需要一个 TypeScript 配置文件 tsconfig.json
,因为 TypeScript 需要使用到 Node.js 的类型信息,模块解析moduleResolution
设置成 node
模式。其它配置项信息可以参考官方文档 [TypeScript]。
{
"compilerOptions": {
"outDir": "./dist/", // path to output directory
"sourceMap": true, // allow sourcemap support
"strictNullChecks": true, // enable strict null checks as a best practice
"module": "es6", // specifiy module code generation
"target": "es5", // specify ECMAScript target version
"allowJs": true, // allow a partial TypeScript and JavaScript codebase
"moduleResolution": "node",
"noImplicitAny": true, // disallow implicit any type
"noImplicitReturns": true,
"strict": true,
},
"include": ["./src/"]
}
安装各种 Loaders 后,需要根据 Loader 开发文档参考配置,Webpack 得配置文件是 webpack.config.js
官方文档有很详细得说明。对于 Loader,主要是配置 rules
规则,test
是文件名匹配正则规则,符合匹配条件得文件就交给指定的 loader
进行处理,各个 Loader 的配置选项参考文档设置。
其中 entry
和 output
是比较重要的配置,entry 表示程序入口,output 表示打包出口,即打包生成的文件。publicPath
是访问资源时使用的参考路径,打包后的资源存放路径与它直接关联。在使用开发服务器时,它就是项目的根目录下的路径,使用相对目录时要参考根目录来设置。output
还可以设置 path
属性来指定打包文件存放位置,默认是 dist
目录。结合 filename
指定输出文件就是 /dist/bin/bundle.js
。如果对资源文件的发布目录有自定义需求,可以通过 process.env.NODE_ENV
变量判端是否是发布编译,然后再指定一个 publicPath
目录。
在 Webpack 4 中,不再强制要求指定 entry 和 output 路径。webpack 4 会默认 entry 为 ./src
,output 为 ./dist
。
mode
模式设置,基本上有 development, production, none 几种模式。根据不同的模式使用不同的配置文件来优化开发/发布。
resolve
设定要解析的文件类型,设置错误解析不到的文件类型会产生 Module not found: Error: Can't resolve...
module.exports = {
// change to .tsx if necessary
entry: './src/index.ts',
mode: 'development',
output: {
publicPath: "/",
filename: './bin/bundle.js'
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".jsx"]
},
module: {
rules: [{
test: /\.(t|j)sx?$/,
use: {
loader: 'ts-loader'
}
}, {
test: /\.(png|jpg|gif)$/i,
use: [{
loader: 'url-loader',
options: {
limit: 8192,
},
}, ],
}, {
test: /\.css$/i,
use: [{
loader: "style-loader"
}, {
loader: 'css-loader',
options: {
modules: true,
}
}]
}]
// },
// devtool: 'inline-source-map',
// devtool: "source-map",
// optimization: {
// minimize: true
// },
// externals: {
// "react": "React",
// "react-dom": "ReactDOM",
}
}
if (process.env.NODE_ENV === "production") {
module.exports.output.publicPath = "./release";
}
如果工程有多个主程序入口文件,那么可以将 entry
和 output
修改成分组打包方式。Webpack 的输出参数 output
指定规则生成输出文件。所有的入口产生的输出文件都必须使用这一套规则,不能针对某一个特定的入口来制定 output
规则。输出项中用 [name]
来引用 entry
每一项中的键值,用以批量指定生成后文件的名称。[hash]
引用本次编译的一个hash版本号,[chunkhash]
引用的是当前chunk的一个hash版本。也就是说,在同一次编译中,每一个chunk的hash都是不一样的;而在两次编译中,如果某个chunk根本没有发生变化,那么该chunk的hash也就不会发生变化。
html-webpack-plugin
和 html-webpack-template
是两个生成 HTML 模板的插件,为了对发布目录 dist
自动清理,可以使用 clean-webpack-plugin
插件。这几个插件都是 Webpack 提供学习如何在 Node.js 平台下做 Webpack 插件开发用的,也具有一定的实用。
Node.js 提供的内置模块 path
可以用来解析绝对路径。
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
// entry: {
// home: ['./home.js', './home.scss'],
// account: ['./account.js', './account.scss']
// },
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Output Management'
})
]
};
先准备一个页面模板 index.html
用它来加载 Webpack 打包生成的输出文件 bundle.js
,为了简化这里就不引用第三方 JavaScript 库:
TypeScript with Webpack
新建 src
目录用来放源代码 index.ts
:
class Student {
fullName: string = "";
readonly age:number = 18;
constructor( public firstName:string, public middle:string, public lastName:string){
this.fullName = firstName + ' ' + middle + ' ' + lastName;
}
}
interface Person {
firstName: string;
lastName: string;
}
function greeter(person: Person) {
return "Hello, " + person.firstName;
}
let user = { firstName: "Jane", lastName: "User" };
// let user = new Student("Jane", "M.", "User");
let $ = (id:string, msg:string) => {
let tag = document.getElementById(id);
if (!tag){
document.body.innerHTML = ("HTML element not found #"+id);
}else{
tag.innerHTML = msg;
}
}
$("content", greeter(user));
其它不需要打包的资源文件单独放放到 public
子目录下,这样的工程目录结构是比较通用合理的。