继上一篇 一步步从零开始用 webpack 搭建一个大型项目 以后的第二篇。本文使用了 webpack5
将项目进行了重构,并全程使用的 webpack-chain
来配置 webpack
,每一个功能也都是独立文件,可单独使用。所以该项目的配置能够在任何项目中被使用。此项目可实战亦可当成 webpack
手册来学习。我开发这个项目的目的就是不管你是新手仍是有经验的大佬均可以从中有所收获。此项目为想学 webpack
的同窗提供了很好的实战平台,每个 插件
每个 loader
都会有详细的讲解及使用背景。javascript
为了节省你们时间,提高学习效率,我想要将全部 webpack
相关的系列都集成在这里,这里的每个优化都是通过我反复推敲实践而来,也会吸收一些优秀的开源库来完善它,此项目将长期维护,也诚挚欢迎全部人参与到该项目当中,一块儿成为该项目的共建者!css
项目地址:github.com/luoxue-vict…html
由于本文是使用 webpack5
进行了重构,那我就先将第 14 课时放在前面讲了前端
本文目录vue
接下来计划去作的 TODO github.com/luoxue-vict…java
本章主要将项目升级到 webpack5,先踩一下坑。把踩坑的通过给你们分享一下。node
webpack5 更像是一个黑盒了,好多以前必需要用插件来完成的工做,如今 webpack5 内部已经被集成了,开箱即用。webpack5 主要为了优化编译速度、更多的默认配置(内置了更多的配置)、更好的代码生成、为未来 webpack 走的更远作铺垫。react
本章概要webpack
本教程能够经过脚手架命令一键升级/降级git
webpack-box upgrade 5/4
复制代码
主要升级了两个插件,其余使用到的模块都已经被兼容,html-webpack-plugin
插件由于涉及到热更新,目前热更新的 bug 尚未修复,因此你们切换到 webpack5
以后的第一次编译能够成功,可是保存后的再次编译会报错(这点我会持续关注,一旦修理当即更新版本)
package.json
{
"html-webpack-plugin": "^4.0.0-beta.11",
"webpack": "^5.0.0-beta.9"
}
复制代码
webpack-box build index
复制代码
Version: webpack 4.41.2
如下是使用了 cache-loader
4216ms
2781ms
2827ms
2797ms
Version: webpack 5.0.0-beta.9
使用了 持久缓存
3567ms
2602ms
2609ms
2582ms
能够看出来 webpack5
使用持久缓存的状况下比 webpack4
使用 cache-loader 的编译速度快了 100ms ~ 200ms
,因此之后就没有必要使用 cache-loader,webpack5 提供了更好的算法跟更优秀的缓存方案
使用持久性缓存时,您再也不须要缓存加载器。与 babel cacheDirectory 相同。
一些错误并修复 error
安装 4.x 版本可修复
npm i html-webpack-plugin@4.0.0-beta.11
复制代码
未修复
第一次编译生效,保存以后会报错,webpack5
对热更新进行了重写,致使 html-webpack-plugin
不兼容,缘由可查
可使用 optimization.chunkIds 进行修改
module.exports = {
//...
optimization: {
chunkIds: "named"
}
};
// 链式修改
config.optimization.set("chunkIds", "natural");
复制代码
以下,在 webpack4 中 a、b 都会被打包进 bundle 中,webpack5 会对嵌套的无用代码也会删除掉,也就是说 b 并不会被打包进 bundle 中了,由于 b 并无被使用到
// inner.js
export const a = 1;
export const b = 2;
// module.js
import * as inner from "./inner";
export { inner };
// user.js
import * as module from "./module";
console.log(module.inner.a);
复制代码
webpack5 会检查都模块内部的方法是否被使用,若是没有被使用的话,那么会把模块内部调用的方法也会被删除
可是前提是你要知道这些代码是无反作用的,否则颇有可能将你的代码删掉,好比你要写一个组件,而你库里并无使用它,那么就有可能在打包的时候被 tree-shaking 了
使用它您须要在 package.json
中配置 "sideEffects": false
,而且设置 optimization.usedExports
为 true
// package.json
{
"sideEffects": false
}
// config/optimization.js
config.optimization.usedExports(true);
复制代码
代码演示
import { something } from "./something";
function usingSomething() {
return something;
}
export function test() {
return usingSomething();
}
复制代码
若是外部模块没有使用 test 方法的话,那么 usingSomething、something 也会在 bundle 中被删除
告诉 webpack webpack 生成代码的最大 EcmaScript 版本
webpack4
仅能支持到 ES5,webpack5
支持 ES5 跟 ES6
ecmaVersion
的取值范围 5 ~ 11 或 2009 ~ 2020,webpack5 默认采用最小值 5
config.output.set("ecmaVersion", 6);
复制代码
webpack5 能够根据不一样类型的文件分别设置 splitChunks 打包的尺寸,默认状况下只针对 javascript 进行分离打包
config.optimization.splitChunks({
minSize: {
javascript: 30000,
style: 50000
}
});
复制代码
webpack5 提供了两种缓存方式,一种是持久缓存将文件缓存在文件系统,另外一种是缓存在内存里
// type {filesystem | memory}
config.cache({
type: "filesystem"
});
复制代码
默认状况下,会被缓存在 node_modules/.cache/webpack
下,您能够经过 cacheDirectory
选项修改缓存目录
本章概要
目的:让职能更加清晰,每一层只作一件事情,使用标准化的 api 去处理同类逻辑
└── api
│── CommandAPI.js
└── PluginAPI.js
└── cwd
│── build:ssr.js
│── build.js
│── dev.js
│── dll.js
│── lint.js
└── ssr:server.js
复制代码
配置 eslint-loader,在 webpack-box dev 时会检测 eslint 规则,若是有报错会显示在控制台上
config.module
.rule("eslint")
.pre()
.exclude.add(/node_modules/)
.end()
.test(/\.(vue|(j)sx?)$/)
.use("eslint-loader")
.loader(require.resolve("eslint-loader"))
.options({
extensions,
cache: true,
cacheIdentifier,
emitWarning: allWarnings,
emitError: allErrors,
eslintPath: path.dirname(
resolveModule("eslint/package.json", cwd) ||
resolveModule("eslint/package.json", __dirname)
),
formatter: loadModule("eslint/lib/formatters/codeframe", cwd, true)
});
复制代码
当咱们项目改变某一个规则时,咱们项目中都会出现大量的错误,咱们确定不但愿手动一个个去修改,因此咱们须要使用 eslint 的自动修复的功能,它可以帮助咱们修复绝大数的错误,还有一些修复不了的咱们再手动修复就能够了
这里写出了部分代码,更多细节能够在项目里面看
packages/eslint/lint.js
const { CLIEngine } = loadModule("eslint", cwd, true) || require("eslint");
const config = Object.assign({
extensions,
fix: true,
cwd
});
const engine = new CLIEngine(config);
const defaultFilesToLint = ["src", "tests", "*.js", ".*.js"].filter(pattern =>
globby
.sync(pattern, { cwd, absolute: true })
.some(p => !engine.isPathIgnored(p))
);
const files = args._ && args._.length ? args._ : defaultFilesToLint;
const report = engine.executeOnFiles(files);
if (config.fix) {
CLIEngine.outputFixes(report);
}
复制代码
咱们但愿经过命令行的形式去修复,webpack-box lint eslint
,因此须要在 cwd
层添加命令行
cwd/lint.js
module.exports = function(injectCommand, api) {
injectCommand(function({ program, cleanArgs, boxConfig }) {
program
.command("lint [type]")
.description("修复lint")
.action(async (name, cmd) => {
const options = cleanArgs(cmd);
const args = Object.assign(options, { name }, boxConfig);
require("../build/lint")(args, api);
});
});
};
复制代码
这样咱们可使用 webpack-box lint eslint
去修复大部分的错误了,去试一下吧~
固然咱们执行 webpack-box lint eslint
命令时能够去修复一些错误,可是当咱们写代码时但愿编译器可以帮助咱们自动修改,而不是等到代码写完了才去校验,这样会给咱们带来二次麻烦,甚至会出现修复不了的问题。
因此咱们使用 vscode
的 eslint
插件来帮助咱们实现吧
首先您必须使用的编译器是 vscode,固然其它的编译器也能够,可是咱们这里只讲 vscode 的配置。
您安装了 eslint 插件后,须要在设置中设置 "eslint.autoFixOnSave": true
,这样就能够在保存时自动修复 eslint
的错误了
固然您可能只在这个项目中使用了 eslint
,而在其余项目中并不须要保存时修复
能够在根目录添加
└── .vscode
└── settings.json
复制代码
放一份我本身的配置供你们参考
{
/*
* @description 编译器配置
* @param tabSize 默认tab为两个空格
* @param formatOnSave 保存时自动修复
*/
"editor.tabSize": 2,
"editor.formatOnSave": true,
/*
* @description eslint 配置
* @param alwaysShowStatus 配置
* @param autoFixOnSave 保存时自动修复
* @param validate 在vue中添加错误提示
*/
"eslint.alwaysShowStatus": true,
"eslint.autoFixOnSave": true,
"eslint.validate": [
"javascript",
"javascriptreact",
{
"language": "vue",
"autoFix": true
}
],
/*
* @description tslint 配置
* @param autoFixOnSave 保存时自动修复
* @param alwaysShowRuleFailuresAsWarnings 全部特征都是用 Warnings
*/
"tslint.autoFixOnSave": true,
"tslint.alwaysShowRuleFailuresAsWarnings": true,
/*
* @description stylelint 配置
* @param autoFixOnSave 保存时自动修复
*/
"stylelint.autoFixOnSave": true,
/*
* @description vetur 配置
*/
"vetur.format.defaultFormatter.html": "prettier",
"vetur.format.defaultFormatterOptions": {
"prettier": {
"semi": false,
"singleQuote": true
}
},
/*
* @description 配置编辑器设置以覆盖某种语言
*/
"[typescript]": {
// "editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "eg2.tslint"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "eg2.tslint"
},
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
复制代码
上述的操做都是咱们理想状态下的检测跟修复,可是有时还会遇到意外的状况,并无 lint 代码就提交了,这样会致使可能出现问题,因此咱们须要在提交代码前进行一次代码检验
在 package.json 中添加 lint-staged,在代码提交时会先执行 lint,lint 经过以后才能提交成功
package.json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx}": ["webpack-box lint eslint", "git add"]
}
}
复制代码
本章概要
使用 stylelint-config-standard 插件
.stylelintrc.js
module.exports = {
root: true,
extends: "stylelint-config-standard"
};
复制代码
module.exports = ({
config,
options: { stylelint: { lintOnSave = false, extensions } = {} },
api
}) => {
const StyleLintPlugin = require("stylelint-webpack-plugin");
const CodeframeFormatter = require("stylelint-codeframe-formatter");
const stylelint = [];
return () => {
config.plugin("stylelint").use(StyleLintPlugin, [
Object.assign(
{
failOnError: lintOnSave === "error",
files: ["src/**/*.{vue,htm,html,css,sss,less,scss}"],
formatter: CodeframeFormatter
},
stylelint
)
]);
};
};
复制代码
module.exports = async function lint({ api, args = {}, pluginOptions = {} }) {
const cwd = api.resolve(".");
const files =
args._ && args._.length
? args._
: [cwd + "/src/**/*.{vue,htm,html,css,sss,less,scss}"];
if (args["no-fix"]) {
args.fix = false;
delete args["no-fix"];
}
const { formatter } = args;
if (
formatter &&
typeof formatter === "string" &&
!["json", "string", "verbose"].includes(formatter)
) {
try {
args.formatter = require(formatter);
} catch (e) {
delete args.formatter;
if (typeof pluginOptions.formatter !== "function") {
console.log(
format(
chalk`{bgYellow.black WARN }`,
chalk`${e.toString()}\n{yellow Invalid formatter}`
)
);
}
}
}
const options = Object.assign(
{},
{
configBasedir: cwd,
fix: true,
files,
formatter: CodeframeFormatter
},
pluginOptions,
normalizeConfig(args)
);
try {
const { errored, results, output: formattedOutput } = await stylelint.lint(
options
);
if (!errored) {
if (!args.silent) {
const hasWarnings = results.some(result => {
if (result.ignored) {
return null;
}
return result.warnings.some(
warning => warning.severity === "warning"
);
});
if (hasWarnings) {
console.log(formattedOutput);
} else {
console.log(
format(
chalk`{bgGreen.black DONE }`,
`stylelint 没有发现错误!${ options.fix ? chalk` {blue (已经自动修复)}` : "" }`
)
);
}
}
} else {
console.log(formattedOutput);
process.exit(1);
}
} catch (err) {
console.log(
format(chalk`{bgRed.black ERROR }`, err.stack.slice(" Error:".length))
);
process.exit(1);
}
};
复制代码
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{vue,htm,html,css,sss,less,scss}": [
"webpack-box lint stylelint",
"git add"
],
"*.{js,jsx}": ["webpack-box lint eslint", "git add"]
}
}
复制代码
本章概要
config/tslintPlugin.js
module.exports = ({
config,
options: { tslint: { lintOnSave = false, useThreads = false } = {} },
api
}) => {
const fs = require("fs");
return () => {
config.plugin("fork-ts-checker").tap(([options]) => {
options.tslint =
lintOnSave !== false && fs.existsSync(api.resolve("tslint.json"));
options.formatter = "codeframe";
options.checkSyntacticErrors = useThreads;
return [options];
});
};
};
复制代码
tslint.json
{
"defaultSeverity": "warning",
"extends": ["tslint:recommended"],
"linterOptions": {
"exclude": ["node_modules/**"]
},
"rules": {
"max-classes-per-file": [true, 5, "exclude-class-expressions"],
"quotemark": [true, "single"],
"semicolon": [true, "never"],
"indent": [true, "spaces", 2],
"ordered-imports": false,
"object-literal-sort-keys": false,
"no-consecutive-blank-lines": false,
"disable-next-line": false,
"only-arrow-functions": false,
"radix": false,
"class-name": false,
"eofline": false,
"no-unused-expression": false,
"no-console": false,
"trailing-comma": false,
"interface-name": false
}
}
复制代码
const { done } = require("@vue/cli-shared-utils");
module.exports = function lint({ args = {}, api, silent }) {
const options = {
fix: args.fix !== false,
formatter: args.format || "codeFrame",
formattersDirectory: args["formatters-dir"],
rulesDirectory: args["rules-dir"]
};
const program = tslint.Linter.createProgram(api.resolve("tsconfig.json"));
const linter = new tslint.Linter(options, program);
const updateProgram = linter.updateProgram;
linter.updateProgram = function(...args) {
updateProgram.call(this, ...args);
patchProgram(this.program);
};
const tslintConfigPath = tslint.Configuration.CONFIG_FILENAMES.map(filename =>
api.resolve(filename)
).find(file => fs.existsSync(file));
const config = tslint.Configuration.findConfiguration(tslintConfigPath)
.results;
const lint = file => {
const filePath = api.resolve(file);
const isVue = isVueFile(file);
patchWriteFile();
linter.lint(filePath, "", isVue ? vueConfig : config);
restoreWriteFile();
};
const files =
args._ && args._.length
? args._
: [cwd + "/src/**/*.ts", cwd + "/src/**/*.vue", cwd + "/src/**/*.tsx"];
return globby(files, { cwd }).then(files => {
files.forEach(lint);
if (silent) return;
const result = linter.getResult();
if (result.output.trim()) {
process.stdout.write(result.output);
} else if (result.fixes.length) {
const f = new tslint.Formatters.ProseFormatter();
process.stdout.write(f.format(result.failures, result.fixes));
} else if (!result.failures.length) {
done("tslint 没有发现错误.\n");
}
if (result.failures.length && !args.force) {
process.exitCode = 1;
}
});
};
复制代码
{
"lint-staged": {
"*.{vue,htm,html,css,sss,less,scss}": [
"webpack-box lint stylelint",
"git add"
],
"*.{ts,tsx}": ["webpack-box lint tslint", "git add"],
"*.{js,jsx}": ["webpack-box lint eslint", "git add"]
}
}
复制代码
在咱们工做中,若是一个文件须要被 copy 到另一个目录下,那么这个文件的引用依赖就可能发生路径错误。还有咱们不喜欢每次引入依赖都要逐层去查找,咱们但愿可以有一个别名来指定某一个目录,不管咱们在哪里使用它。
本章概要
src/main.js
import { cube } from "./treeShaking";
import { cube } from "@/treeShaking";
import { cube } from "@src/treeShaking";
复制代码
alias: {
'@': resolve('src'),
'@src': resolve('src')
}
复制代码
module.exports = ({ config, options, resolve }) => {
const fs = require("fs");
const conf = options.alias;
return () => {
// 生成默认别名
const dirs = fs.readdirSync(resolve("src"));
let aliasConfig = config.resolve.extensions
.merge([".mjs", ".js", ".jsx", ".vue", ".ts", ".json", ".wasm"])
.end().alias;
dirs.forEach(v => {
const stat = fs.statSync(resolve(`src/${v}`));
if (stat.isDirectory()) {
aliasConfig = aliasConfig.set(`@${v}`, resolve(`src/${v}`));
}
});
// 用户配置别名
if (conf.alias) {
const keys = Object.keys(conf.alias);
keys.forEach(key => {
aliasConfig = aliasConfig.set(key, conf.alias[key]);
});
}
// 自定义设置别名
aliasConfig.set("@", resolve("src")).set("@src", resolve("src"));
};
};
复制代码
若是您使用的是 ts 的话,那么配置别名了以后会失去类型,提示找不到模块,因此咱们须要在编译器配置对应的别名才能够
tsconfig.json/jsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"rootDir": ".",
"paths": {
"@src/*": [
"src/*"
],
"@/*": [
"src/*"
],
}
}
}
复制代码
有时咱们须要在脚手架跟业务代码之间有一个通讯的桥梁
好比咱们 npm run build
时咱们是运行的生产环境,我想在 main.js
中生产环境下作一些特殊的逻辑。可是 main.js
执行是在浏览器端,而 npm run build
时运行在 node
端,两端没有办法作通信。那么咱们怎么办呢?
webpack
给咱们提供了一个插件 EnvironmentPlugin
,这个插件能够将咱们在 node
端定义的变量,在编译时将值编译到代码中,举个例子
咱们在 main.js
中写了一段 node
中看起来很常见的代码,可是这在浏览器中是不能识别的,由于浏览器中并无 process
对象,这段代码不出意外会报错
main.js
if (process.env.NODE_ENV === "production") {
console.log("Welcome to production");
}
复制代码
咱们配置 webpack.EnvironmentPlugin
插件
const webpack = require("webpack");
module.exports = ({ config, resolve, options }) => {
return () => {
const resolveClientEnv = require("../util/resolveClientEnv");
config
.plugin("process-env")
.use(webpack.EnvironmentPlugin, [resolveClientEnv(options)]);
};
};
复制代码
util/resolveClientEnv.js
module.exports = function resolveClientEnv(options, raw) {
const env = {};
if (process.env) {
Object.keys(process.env).forEach(key => {
if (key === "NODE_ENV") {
env[key] = process.env[key];
}
});
}
if (options.env) {
Object.assign(env, options.env);
}
return env;
};
复制代码
咱们执行 npm run build
,看一下 dist/index.bundle.js
会编译成什么
// "production" === "production"
if (true) {
console.log("Welcome to production");
}
复制代码
webpack
将 process.env.NODE_ENV
的值编译在 bundle
中,这样咱们就能够在 web
端运行了,并且编译出来是在生产环境下
有时咱们常常会出现这样的状况,明明本地编译没有问题,可是上线 jenkins 编译的时候就会报错,这种问题每每会花费咱们较长的时间才能发现这个 bug,原来是本地路径的大小写出现了问题,引用路径时咱们本地是不区分大小写的。举个例子
└──── src
│── Index.js
└── main.js
复制代码
上面的路径中 Index.js 的首字母是大写,可是我在 main.js 用小写去引用它
main.js
import Index from "./index.js";
复制代码
这样在本地是不会报错的,可是当你用 Jenkins 上线的时候,就会报错找不到 ./index.js 模块
因此咱们须要一个插件,在咱们开发时就严格检查大小写,这样就不会出现这样的问题了。
咱们使用 case-sensitive-paths-webpack-plugin
插件来实现它
module.exports = ({ config, webpackVersion, resolve, options }) => {
return () => {
// webpack 5 不兼容
if (parseInt(webpackVersion) >= 5) return;
config
.plugin("case-sensitive-paths")
.use(require("case-sensitive-paths-webpack-plugin"));
};
};
复制代码
这章就直接上代码吧,是以前基础篇的补充
module.exports = ({ config, webpackVersion, resolve, options }) => {
return () => {
const getAssetPath = require("../util/getAssetPath");
const inlineLimit = 4096;
const genAssetSubPath = dir => {
return getAssetPath(
options,
`${dir}/[name]${options.filenameHashing ? ".[hash:8]" : ""}.[ext]`
);
};
const genUrlLoaderOptions = dir => {
return {
limit: inlineLimit,
fallback: {
loader: "file-loader",
options: {
name: genAssetSubPath(dir)
}
}
};
};
config.module
.rule("images")
.test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
.use("url-loader")
.loader(require.resolve("url-loader"))
.options(genUrlLoaderOptions("img"));
config.module
.rule("svg")
.test(/\.(svg)(\?.*)?$/)
.use("file-loader")
.loader(require.resolve("file-loader"))
.options({
name: genAssetSubPath("img")
});
config.module
.rule("media")
.test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/)
.use("url-loader")
.loader(require.resolve("url-loader"))
.options(genUrlLoaderOptions("media"));
config.module
.rule("fonts")
.test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i)
.use("url-loader")
.loader(require.resolve("url-loader"))
.options(genUrlLoaderOptions("fonts"));
};
};
复制代码
在书写 css 时,咱们会将经常使用到的函数/变量等封装成一个 global.less/scss,而后在咱们用到的时候将其引入。显然每次都要手动引入变得很麻烦,也容易出错(尤为组内来新人的时候),因此咱们想若是把 global 自动引入到文件中不就完美了吗?
咱们须要一个 style-resources-loader
来帮助咱们完成这件事
config/styleResourceLoader.js
module.exports = ({ config, options }) => {
const resourcesOpt = options.resources;
return () => {
["normal"].forEach(oneOf => {
Object.keys(resourcesOpt).forEach(loader => {
config.module
.rule(loader)
.oneOf(oneOf)
.use("style-resources-loader")
.loader("style-resources-loader")
.options({
patterns: resourcesOpt[loader].patterns
});
});
});
};
};
复制代码
config/style.js
if (loader) {
let resolvedLoader;
try {
resolvedLoader = require.resolve(loader);
} catch (error) {
resolvedLoader = loader;
}
rule
.use(loader)
.loader(resolvedLoader)
// options 是对应 config 中的 css 参数,能够自行配置对应loader的参数
.options(Object.assign({ sourceMap }, options));
}
复制代码
box.config.js
{
"css": {
"sourceMap": true, // 是否开启css source map
"loaderOptions": { // 配置loader的options
"css": {},
"less": {
"globalVars": { // less 设置全局变量
"gray": "#ccc"
}
},
"sass": {},
"postcss": {},
"stylus": {}
},
"isCssModule": false, // 是否对css进行模块化处理
"needInlineMinification": false // 是否须要压缩css
},
"resources": {
"less": {
"patterns": [path.resolve(__dirname, "./src/global/*.less")]
},
"scss": {
"patterns": [path.resolve(__dirname, "./src/global/*.scss")]
}
}
}
复制代码
└──── src
│── global
│ │── index.less
│ └── index.scss
└── style
└── index.less
复制代码
global/index.less
.g-less-height () {
height: 100%;
}
.g-less-test {
width: 100%;
}
复制代码
style/index.less
.test {
width: 300px;
color: @gray;
.g-less-height();
}
复制代码
style/index.scss
.g-scss-test {
width: 100%;
}
复制代码
dist/css/index.css
.g-less-test {
width: 100%;
}
.test {
color: #ccc;
width: 100%;
height: 100%;
}
.g-scss-test {
width: 100%;
}
复制代码
可见全局的样式都被打包进 dist/css/index.css
中了,咱们不再用每次都手动引入了
放在根目录,开启自动修复 eslint/tslint/stylelint 功能
.vscode/setting.json
{
/* * @description 编译器配置 * @param tabSize 默认tab为两个空格 * @param formatOnSave 保存时自动修复 */
"editor.tabSize": 2,
"editor.formatOnSave": true,
/* * @description eslint 配置 * @param alwaysShowStatus 配置 * @param autoFixOnSave 保存时自动修复 * @param validate 在vue中添加错误提示 */
"eslint.alwaysShowStatus": true,
"eslint.autoFixOnSave": true,
"eslint.validate": [
"javascript",
"javascriptreact",
{
"language": "vue",
"autoFix": true
}
],
/* * @description tslint 配置 * @param autoFixOnSave 保存时自动修复 * @param alwaysShowRuleFailuresAsWarnings 全部特征都是用 Warnings */
"tslint.autoFixOnSave": true,
"tslint.alwaysShowRuleFailuresAsWarnings": true,
/* * @description stylelint 配置 * @param autoFixOnSave 保存时自动修复 */
"stylelint.autoFixOnSave": true,
/* * @description vetur 配置 */
"vetur.format.defaultFormatter.html": "prettier",
"vetur.format.defaultFormatterOptions": {
"prettier": {
"semi": false,
"singleQuote": true
}
},
/* * @description 配置编辑器设置以覆盖某种语言 */
"[typescript]": {
// "editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "eg2.tslint"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "eg2.tslint"
},
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
复制代码
配合 lint 作一些,代码样式上的格式化
prettier.config.js
/** * pretiier 标准配置 */
module.exports = {
// 在ES5中有效的结尾逗号(对象,数组等)
trailingComma: "es5",
// 不使用缩进符,而使用空格
useTabs: false,
// tab 用两个空格代替
tabWidth: 2,
// 仅在语法可能出现错误的时候才会添加分号
semi: false,
// 使用单引号
singleQuote: true,
// 在Vue文件中缩进脚本和样式标签。
vueIndentScriptAndStyle: true,
// 一行最多 100 字符
printWidth: 100,
// 对象的 key 仅在必要时用引号
quoteProps: "as-needed",
// jsx 不使用单引号,而使用双引号
jsxSingleQuote: false,
// 大括号内的首尾须要空格
bracketSpacing: true,
// jsx 标签的反尖括号须要换行
jsxBracketSameLine: false,
// 箭头函数,只有一个参数的时候,也须要括号
arrowParens: "always",
// 每一个文件格式化的范围是文件的所有内容
rangeStart: 0,
rangeEnd: Infinity,
// 不须要写文件开头的 @prettier
requirePragma: false,
// 不须要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: "preserve",
// 根据显示样式决定 html 要不要折行
htmlWhitespaceSensitivity: "css",
// 换行符使用 lf
endOfLine: "lf"
};
复制代码
至此webpack系列2已经结束了,更精彩的还在后面,前面两篇文章只能算是为后来的大项目作铺垫,后续我会使用lerna进行重构,将使用插件化管理,构建插件化生态令人人有益。后面会构建vue跟react项目,甚至小程序,跨端等都会被集成进来。
您能够将您想的想法提到这里面 github.com/luoxue-vict… 我会认真对待每个issue
最后感谢你们的支持!我叫技匠
能够微信公众号搜索前端技匠
,也能够加我微信 luoxue2479
进群交流。