开发相关版本:
-
webpack: 5.51.1版本
-
webpack-cli: 4.8.0版本
-
node: 14.10.0版本
完整项目地址webpack-learning
1. 初始化项目
npm init -y
注: -y的含义: yes, 在init的时候省去敲回车的步骤,生成的默认的package.json。
接着安装webpack、webpack-cli
npm i -D webpack webpack-cli
注: npm i -D是npm install --save-dev的缩写, npm i -S是npm install --save的缩写
新建一个文件夹src,还是使用之前两个js:
greeting.js
export function greeting (name) {
return "hello " + name;
}
index.js
import { greeting } from './greeting.js';
document.write(greeting('world!'))
在package.json里配置打包命令:
"scripts": {
"build": "webpack ./src/index.js"
},
执行:
npm run build
如果生成了一个dist文件夹,并且内部含有main.js说明已经打包成功了
完整代码地址demo01-init
2. 新增webpack.config.js
新建config文件夹,接着新建一个webpack.config.js
const path = require("path");
module.exports = {
mode: "development", // 开发模式
entry: "./src/index.js", // 入口文件
output: {
filename: "main.js", // 打包后的文件名称
path: path.resolve(__dirname, "../dist") // 打包后的目录
}
};
更改我们的打包命令
"scripts": {
"build": "webpack --config ./config/webpack.config.js"
},
执行npm run build进行打包
完整代码地址demo02-config
3.配置html模板
3.1 单入口配置
安装html-webpack-plugin
npm i -D html-webpack-plugin
利用html-webpack-plugin去将webpack打包出来的js文件引入到定义好的html模板中, 在根目录新建public,里面新建一个index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>HtmlWebpackPlugin</title>
</head>
<body>
<noscript>
<strong>We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
</body>
</html>
修改webpack.config.js
引入html-webpack-plugin,加上plugins
const HtmlWebpackPlugin = require("html-webpack-plugin");
//...
module.exports = {
// ....
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html")
})
]
}
执行npm run build就可以看到main.js已经引入到html页面中:
如果我们需要频繁的修改js,但要实时查看效果的话则需要引入webpack-dev-server来解决这个问题。
webpack-dev-server
为你提供了一个基本的 web server,并且具有 live reloading(实时重新加载) 功能
npm i -D webpack-dev-server
修改配置文件,告知 dev server,从什么位置查找文件:
同时配置热更新
webpack.config.js
const path = require("path");
const webpack = require('webpack');
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development", // 开发模式
entry: path.resolve(__dirname,'../src/index.js'), // 入口文件
// web server
devServer: {
static: {
directory: path.resolve(__dirname, '../dist'), // 打包后的文件路径
},
open: true, //自动打开浏览器
compress: true, //启动gzip压缩
port: 9000 // 端口号
},
output: {
filename: "main.js", // 打包后的文件名称
path: path.resolve(__dirname, "../dist"), // 打包后的目录
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html")
}),
new webpack.HotModuleReplacementPlugin()
]
};
接着修改package.json里配置命令:
"scripts": {
"dev": "webpack-dev-server --config ./config/webpack.config.js",
"build": "webpack --config ./config/webpack.config.js"
},
为了在浏览器里看效果,我们可以修改src/index.js
import { greeting } from "./greeting.js";
document.getElementById("app").textContent = greeting("world!");
执行npm run dev就可以启动浏览器看实际效果了:
完整代码地址demo03-singlepage
3.2 多入口配置
还是利用html-webpack-plugin来解决, 只是生成多个html-webpack-plugin实例来解决这个问题
在src文件夹下新建search.js
search.js
document.getElementById("app").textContent = "我是search页面";
修改webpack.config.js里面的plugins
// ...
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
template:path.resolve(__dirname,'../public/index.html'),
filename:'index.html',
chunks:['main']
}),
new HtmlWebpackPlugin({
template:path.resolve(__dirname,'../public/index.html'),
filename:'search.html',
chunks:['search'] // 与入口文件对应的模块名
})
]
}
先执行npm run build看下打包后生成的文件:
执行npm run dev看下实际效果:
两个页面都正常
完整代码地址demo04-multipage
3.3 利用glob对页面需要打包文件的路径进行处理
glob 在webpack中对文件的路径处理非常之方便,比如当搭建多页面应用时就可以使用glob对页面需要打包文件的路径进行很好的处理
安装glob
npm i -D glob
我们先调整下src中目录结构
然后在根目录新建一个getMultiPageConfig.js
getMultiPageConfig.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const glob = require("glob");
exports.getMultiPageConfig = function () {
const entries = {}, htmlPlugins = [];
// glob同步方法获取
const entryFiles = glob.sync(path.resolve(__dirname, "./src/js/*/index.js"));
// ['/xxx/demo05-glob-multpage/src/js/index/index.js', '/xxx/demo05-glob-multpage/src/js/search/index.js']
entryFiles.forEach((ele) => {
let match = ele.match(/src\/js\/(.*)\/index\.js/);
let pageName = match && match[1];
if (pageName) {
entries[pageName] = ele;
htmlPlugins.push(
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "./public/index.html"),
filename: `${pageName}.html`,
chunks: [pageName],
})
);
}
});
return {
entries,
htmlPlugins,
};
};
接着修改webpack.config.js
const path = require("path");
const { getMultiPageConfig } = require("../getMultiPageConfig");
const { entries, htmlPlugins } = getMultiPageConfig();
module.exports = {
mode: "development", // 开发模式
// 入口文件
entry: entries,
// web server
devServer: {
static: {
directory: path.resolve(__dirname, '../dist'), // 打包后的文件路径
},
open: true, //自动打开浏览器
compress: true, //启动gzip压缩
port: 9000 // 端口号
},
output: {
filename: "[name].js", // 打包后的文件名称
path: path.resolve(__dirname, "../dist"), // 打包后的目录
},
plugins: htmlPlugins
};
执行npm run dev/build结果都正常
完整代码地址demo05-glob-multpage
3.4 webpack5之前加入clean-webpack-plugin清理打包目录
使用 clean-webpack-plugin清除dist文件夹
安装 clean-webpack-plugin
npm i -D clean-webpack-plugin
接着修改webpack.config.js , 引入clean-webpack-plugin,及修改plugins
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
//...
module.exports = {
// ....
plugins: [new CleanWebpackPlugin()].concat(htmlPlugins)
}
3.5 webpack5 配置output.clean清理打包目录
//...
module.exports = {
// ....
output: {
clean: true,
// ...
}
}
完整代码地址demo05-glob-multpage
4.引入CSS
4.1 引入css、less、scss等
先修改public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>webpack learning</title>
</head>
<body>
<noscript>
<strong>We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<h1>Hello World!</h1>
<h2>Hello World!</h2>
<h3>Hello World!</h3>
<h4>Hello World!</h4>
<h5 id="title"></h5>
</body>
</html>
我们在src里创建assets文件夹, 接着在该文件夹下创建index.css、index.less、index.scss, search.less
index.css
h1 {
color: red;
}
index.less
@color: blue;
h2 {
color: @color;
}
index.scss
$color:yellow;
h3 {
color: $color;
}
search.less
@color:wheat;
h1, h2, h3 {
color: @color;
}
修改src/index/index.js
index.js
import { greeting } from "./greeting.js";
import '../../assets/index.css';
import '../../assets/index.less';
import '../../assets/index.scss';
document.getElementById('title').textContent = greeting("world!");
由于webpack开箱即用只支持JS和JSON两种文件类型,此时我们需要利用style-loader、css-loader来解析css文件, 利用less、less-loader来解析.less文件,利用sass、sass-loader来解析.scss文件
我们一次性安装这些loader
npm i -D style-loader css-loader less less-loader sass sass-loader
接着修改webpack.config.js
//...
module.exports = {
// ....
module:{
rules:[
{
test:/\.css$/,
use:['style-loader','css-loader'] // 从右向左解析原则
},
{
test:/\.less$/,
use:['style-loader','css-loader','less-loader'] // less的loader
},
{
test:/\.scss$/,
use:['style-loader','css-loader','sass-loader'] // scss的loader
}
]
}
}
执行npm run dev,可以看出引入的样式文件都以style的方式引入到html页面中
完整代码地址demo06-css
4.2 拆分css成独立的文件
webpack 4.0以前,我们通过extract-text-webpack-plugin
插件,把css样式从js文件中提取到单独的css文件中。webpack4.0以后,官方推荐使用mini-css-extract-plugin
插件来打包css文件
安装mini-css-extract-plugin
npm i -D mini-css-extract-plugin
修改配置如下:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
//...
module.exports = {
// ....
plugins: [
new CleanWebpackPlugin(),
...htmlPlugins,
new MiniCssExtractPlugin({
filename: "[name].[hash].css",
chunkFilename: "[id].css",
}),
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"], // 从右向左解析原则
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
},
{
test: /\.scss$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
}
]
}
}
执行npm run build,css被打包出单独成一个文件
执行npm run dev
完整代码地址demo07-extract
4.3 为css添加浏览器前缀
利用postcss-loader、autoprefixer来为css添加浏览器前缀
安装postcss-loader、autoprefixer
npm i -D postcss-loader autoprefixer
为了看实际效果,我们在css文件里加animation样式:
src/index/index.less
@color: blue;
h2 {
color: @color;
}
.test {
width: 100px;
height: 100px;
background: red;
position: relative;
animation: mymove 5s infinite;
}
@keyframes mymove {
from {
left: 0px;
}
to {
left: 200px;
}
}
修改配置webpack.config.js
//...
module.exports = {
// ....
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"], // 从右向左解析原则
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [["autoprefixer"]],
},
},
},
"less-loader",
], // less的loader
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [["autoprefixer"]],
},
},
},
"sass-loader",
], // scss的loader
}
],
}
}
执行npm run dev 可以看到animation样式已经加了浏览器前缀
完整代码地址demo08-autoprefixer
5.打包图片、媒体、字体等文件
在 webpack5 之前,我们一般都会使用以下几个 loader 来处理一些常见的静态资源,比如 PNG 图片、SVG 图标等等,他们的最终的效果大致如下所示:
-
raw-loader
:允许将文件处理成一个字符串导入 -
file-loader
:将文件打包导到输出目录,并在 import 的时候返回一个文件的 URI -
url-loader
:当文件大小达到一定要求的时候,可以将其处理成 base64 的 URIS
先来看看webpack5之前处理图片、媒体、字体等文件的方法
5.1 webpack5之前解决引用文件路径等问题
file-loader
就是将文件在进行一些处理后(主要是处理文件名和路径、解析文件url),并将文件移动到输出的目录中
安装file-loader
npm i -D file-loader
在assets里引入test.jpg、test1.jpg、movie.ogg等资源
修改src/index/index.js
import { greeting } from "./greeting.js";
import '../../assets/index.css';
import '../../assets/index.less';
import '../../assets/index.scss';
// 在js中使用图片
import test from "../../assets/test.jpg";
import test1 from "../../assets/test1.jpg";
import movie from "../../assets/movie.ogg";
document.getElementById('title').textContent = greeting("world!");
let img = new Image();
img.src = test;
document.getElementById("testImg").appendChild(img);
let img1 = new Image();
img1.src = test1;
document.getElementById("testImg").appendChild(img1);
let videoDom = document.createElement("video");
videoDom.src = movie;
videoDom.controls = "controls"
document.getElementById("testMedia").appendChild(videoDom);
修改配置文件,加上该loader
//...
module.exports = {
// ....
module: {
rules: [
{
test: /\.(gif|png|jpe?g)$/i,
use: [
{
loader: "file-loader",
options: {
name: "[path][name].[ext]",
},
},
],
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
use: [
{
loader: "file-loader",
options: {
name: "[path][name].[ext]",
},
},
],
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
use: [
{
loader: "file-loader",
options: {
name: "[path][name].[ext]",
},
},
],
},
],
}
}
执行npm run build 效果如下:
完整代码地址demo09-file
5.2 webpack5之前优化图片等问题
url-loader 一般与file-loader搭配使用,功能与 file-loader 类似,如果文件小于限制的大小。则会返回 base64 编码,否则使用 file-loader 将文件移动到输出的目录中
安装url-loader
npm i -D url-loader
修改配置文件,
//...
module.exports = {
// ....
module: {
rules: [
{
test: /\.(gif|png|jpe?g)$/i,
use: [
{
loader: "url-loader",
options: {
limit: 1024 * 8, // // 将小于8KB的图片转换成base64的格式
fallback: {
loader: "file-loader",
options: {
name: "img/[name].[hash:8].[ext]", // 文件名.hash.文件扩展名 默认格式为[hash].[ext]
},
},
},
},
],
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
use: [{
loader: 'file-loader',
options: {
name: 'media/[name].[ext]'
}
}]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
use: [
{
loader: "file-loader",
options: {
name: "[path][name].[ext]",
},
},
],
},
],
}
}
执行npm run dev可以看出小于8kb的图片base64编码了
完整代码地址demo10-url
5.3 webpack5 内置的 Asset Modules
如今webpack5 提供了内置的静态资源构建能力,我们不需要安装额外的 loader,仅需要简单的配置就能实现静态资源的打包和分目录存放。如下:满足规则匹配的资源就能够被存放在 assets 文件夹下面:
asset/resource
发送一个单独的文件并导出 URL。之前通过使用file-loader
实现。asset/inline
导出一个资源的 data URI。之前通过使用url-loader
实现。asset/source
导出资源的源代码。之前通过使用raw-loader
实现。asset
在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用url-loader
,并且配置资源体积限制实现(默认为8KB)。
修改配置文件
//...
module.exports = {
// ....
module: {
rules: [
{
test: /\.(gif|png|jpe?g)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024,
},
},
generator: {
filename: 'assets/[hash:8].[name][ext]',
},
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
type: 'asset/resource',
generator: {
filename: 'assets/[name][ext]',
}
},
],
}
}
执行npm run build 效果如下
完整代码地址demo10-asset
6. babel转ES5
在webpack中 默认只能处理部分 ES6的新语法,一些更高级的ES6或ES7的语法,webpack是处理不了的这个时候就需要借助第三方的loader 来帮助webpack 处理这些高级的语法。
Balel 可以帮我我们将高级的语法转为低级的语法
利用babel-loader可以使我们的js代码兼容更多的环境
安装babel-loader @babel/preset-env @babel/core
npm i -D babel-loader @babel/preset-env @babel/core
修改src/index/index.js,加上一些ES6方法
//...省略部分代码
// 箭头函数
let arrowFn = () => console.log("hello babel");
arrowFn();
// 解构赋值
let arr = [1, 2, 3];
let [a, b, c] = arr;
// 拓展运算符
console.log(...arr);
//
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach((x) => s.add(x));
for (let i of s) {
console.log(i);
}
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, "done");
});
}
timeout(100).then((value) => {
console.log(value);
});
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
asyncPrint("hello world", 50);
修改配置文件,加上babel-loader
//...
module.exports = {
// ....
module: {
rules: [
{
test:/\.js$/,
use:{
loader:'babel-loader',
options:{
presets:['@babel/preset-env']
},
},
exclude: /node_modules/
},
],
}
}
在npm run dev执行完后发现一个错误
这个报错表面上是由于 async function 语法被 babel 转译之后的代码使用了 regeneratorRuntime 这个变量,但是这个变量在最终的代码里未定义造成的报错。
babel 在转译的时候,会将源代码分成 syntax 和 api 两部分来处理:
- syntax:类似于展开对象、optional chain、let、const 等语法
- api:类似于 [1,2,3].includes 等函数、方法
上面的babel-loader
只会将 ES6/7/8语法转换为ES5语法,但是对新api并不会转换 例如(promise、Generator、Set、Maps、Proxy等)
此时我们需要借助babel-polyfill来帮助我们转换
安装babel-polyfill
npm i @babel/polyfill
接着在src/index/index.js引入该文件
import "@babel/polyfill";
// ...省略以下代码
执行打包,一切正常,但在这种模式下,babel 会将所有的 polyfill 全部引入导致打包体积过大,而且需要在头部引入该文件,正确的做法是使用按需加载,@babel/preset-env 中有一个配置选项 useBuiltIns,用来告诉 babel 如何处理 api。将 useBuiltIns 改为 "usage",babel 就可以按需加载 polyfill,并且不需要手动引入 @babel/polyfill
修改配置文件:
//...
module.exports = {
// ....
module: {
rules: [
{
test:/\.js$/,
use:{
loader:'babel-loader',
options:{
presets:['@babel/preset-env']
},
},
exclude: /node_modules/
},
],
}
}
同时去掉src/index/index.js中import "@babel/polyfill";
执行npm run dev看下打包完的index.js
可以看出是按需加载的
完整代码地址demo11-babel
7. SplitChunksPlugin
SplitChunksPlugin
插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。
使用 optimization.splitChunks
配置选项
//...
module.exports = {
// ....
optimization: {
splitChunks: {
chunks: 'all', // 设置为all, chunk可以在异步和非异步chunk之间共享; async 代码分割时对异步代码生效; inital:同步代码有效
minSize: 20000, // 代码分割最小的模块大小,引入的模块大于 30000B 才做代码分割
minRemainingSize: 0,
minChunks: 1, // 引入的次数大于等于1时才进行代码分割
maxAsyncRequests: 6, // 按需加载时的最大并行请求数
maxInitialRequests: 4, // 入口点的最大并行请求数
automaticNameDelimiter: '~', // 文件生成时的连接符
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/, // 位于node_modules中的模块做代码分割
priority: -10, // 根据优先级决定打包到哪个组里,例如一个 node_modules 中的模块进行代码
reuseExistingChunk: true,
}, // 分割,,既满足 vendors,又满足 default,那么根据优先级会打包到 vendors 组中。
default: { // 没有 test 表明所有的模块都能进入 default 组,但是注意它的优先级较低。
minChunks: 2,
priority: -20, // 根据优先级决定打包到哪个组里,打包到优先级高的组里。
reuseExistingChunk: true // //如果一个模块已经被打包过了,那么再打包时就忽略这个上模块
}
}
},
},
}
完整代码地址demo12-splitChunk
8. TypeScript
安装 TypeScript compiler 和 loader
npm i -D typescript ts-loader
如果使用第三方类库 一定要记得同时安装此库的类型声明文件
修改配置
//...
module.exports = {
// ....
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
}
还需要新增tsconfig.json
{
// 用来配置编译选项
"compilerOptions": {
"outDir": "./dist/", // 用来指定编译后文件所在的目录
"noImplicitAny": true,
"module": "es6", // 生成的module的形式,esm,cmd,amd
"target": "es5", // 生成js 的版本
"jsx": "react", // jsx用于的开发环境,preserve/react/RN
"strict": false, // 是否严格模式
"allowJs": true, // 是否对js文件进行编译,默认是false
"checkJs": true, // 检查js代码是否符合逻辑规范,默认是false
"removeComments": true, //是否移除注释
"moduleResolution": "node", // 用于选择模块解析策略 node/classic
"importHelpers": true, // 指定是否引入tslib里的复制工具函数
"experimentalDecorators": true, // 用于指定是否启用实验性的装饰器特性
"esModuleInterop": true, // 通过导入内容创建命名空间,实现CommonJS和ES模块之间的互操作性
"allowSyntheticDefaultImports": true, // 用于允许从没有默认导出的模块中默认导入
"sourceMap": true, // 编译时是否生成.map文件
"baseUrl": ".",// 用于设置解析非相对模块名称的基本目录,相对模块不会受到baseUrl的影响
//用于指定需要包含的模块,只有在这里列出的模块的声明文件才会被加载
"types": [],
// 用于设置模块名到基于baseUrl的路径映射
"paths": {
"@/*": [
"src/*"
]
},
// 指定要包含在编译中的库文件
"lib": [
"dom",
"es5",
"es6",
"es2018.promise"
]
},
// 指定编译的文件,没有include和exclude时候用
"file": [],
// 指定待编译的文件
"include": [
"src/**/*.ts",
],
// 指定排除的文件
"exclude": [
"node_modules"
]
}
完整代码地址demo13-ts
9. 搭建vue开发环境
9.1 解析vue文件
vue-loader解析.vue文件, vue-template-compiler
用于编译模板
安装vue-loader、vue-template-compiler
npm i -D vue-loader vue-template-compiler
在src文件里新建App.vue,index.js
App.vue
<template>
<div id="app">{{message}}</div>
</template>
<script>
export default {
name: "App",
data() {
return {
message: "hello world!"
}
}
}
</script>
index.js
import Vue from "vue";
import App from "./App";
new Vue({
el: '#app',
render: h => h(App)
})
修改配置文件
主要是配置resolve选项,该选项能设置模块如何被解析
const path = require("path");
const { VueLoaderPlugin } = require("vue-loader");
const webpack = require('webpack');
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development", // 开发模式
entry: path.resolve(__dirname, "../src/index.js"), // 入口文件
// web server
devServer: {
static: {
directory: path.resolve(__dirname, "../dist"), // 打包后的文件路径
},
open: true, //自动打开浏览器
compress: true, //启动gzip压缩
port: 9000, // 端口号
},
output: {
clean: true, // 清理 /dist 文件夹
filename: "main.js", // 打包后的文件名称
path: path.resolve(__dirname, "../dist"), // 打包后的目录
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
}),
],
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
},
],
},
// 配置模块如何解析
resolve: {
alias: {
vue$: "vue/dist/vue.runtime.esm.js", // 末尾添加 $,以表示精准匹配
" @": path.resolve(__dirname, "../src"),
},
extensions: ["*", ".js", ".json", ".vue"], // 尝试按顺序解析这些后缀名。如果有多个文件有相同的名字,但后缀名不同,webpack 会解析列在数组首位的后缀的文件 并跳过其余的后缀
}
};
完整代码地址demo14-vue
9.2 集成vue + typescript开发环境
安装插件
npm i -S vue-class-component vue-property-decorator
npm i -D ts-loader typescript
- vue-class-component:强化 Vue 组件,使用 TypeScript/装饰器 增强 Vue 组件
- vue-property-decorator:在 vue-class-component 上增强更多的结合 Vue 特性的装饰器
- ts-loader:为了让webpack识别 .ts .tsx文件
配置 webpack
修改entry及配置rules、resolve
const path = require("path");
const { VueLoaderPlugin } = require("vue-loader");
const webpack = require('webpack');
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development", // 开发模式
entry: path.resolve(__dirname, "../src/index.ts"), // 入口文件
// web server
devServer: {
static: {
directory: path.resolve(__dirname, "../dist"), // 打包后的文件路径
},
open: true, //自动打开浏览器
compress: true, //启动gzip压缩
port: 9000, // 端口号
},
output: {
clean: true, // 清理 /dist 文件夹
filename: "main.js", // 打包后的文件名称
path: path.resolve(__dirname, "../dist"), // 打包后的目录
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
}),
],
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
"babel-loader",
{
loader: "ts-loader",
options: { appendTsxSuffixTo: [/\.vue$/] }
}
]
},
],
},
// 配置模块如何解析
resolve: {
alias: {
vue$: "vue/dist/vue.runtime.esm.js", // 末尾添加 $,以表示精准匹配
" @": path.resolve(__dirname, "../src"),
},
extensions: [".js", ".json", ".vue", '.ts', '.tsx',], // 尝试按顺序解析这些后缀名。如果有多个文件有相同的名字,但后缀名不同,webpack 会解析列在数组首位的后缀的文件 并跳过其余的后缀
}
};
以及加上tsconfig.js,同配置ts配置
让 ts 识别 .vue,在src目录下新建文件vue-shim.d.ts ,在文件中输入代码
// 意思是告诉 TypeScript *.vue 后缀的文件可以交给 vue 模块来处理
declare module '*.vue' {
import Vue from 'vue';
export default Vue;
}
改造App.vue
- 在
script
标签上加上lang="ts"
, 意思是让webpack
将这段代码识别为typescript
而非javascript
- 修改vue组件的构造方式,详见官方
- 用
vue-property-decorator
语法改造之前代码
<template>
<div id="app">{{message}}</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
@Component({
name: 'App'
})
export default class extends Vue {
message = "hello world!"
}
</script>
而在代码中导入 *.vue
文件的时候,需要写上 .vue
后缀。原因还是因为 TypeScript
默认只识别 *.ts
文件,不识别 *.vue
文件
修改index.ts中 import App from "./App.vue";
完整代码地址demo15-vueTs
参考资料: