if (
Object.prototype.hasOwnProperty.call(installedChunks, chunkId) &&
installedChunks[chunkId]
) {
resolves.push(installedChunks[chunkId][0]);
}
// 模块安装完
installedChunks[chunkId] = 0;
}
for (moduleId in moreModules) {
if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
modules[moduleId] = moreModules[moduleId];
}
}
if (parentJsonpFunction) parentJsonpFunction(data);
while (resolves.length) {
// 执行所有 promise 的 resolve 函数
resolves.shift()();
}
}
function jsonpScriptSrc(chunkId) {
return webpack_require.p + ‘’ + ({}[chunkId] || chunkId) + ‘.bundle.js’;
}
function webpack_require(moduleId) {
// …
}
webpack_require.e = function requireEnsure(chunkId) {
var promises = [];
// …
var script = document.createElement(‘script’);
var onScriptComplete;
script.charset = ‘utf-8’;
script.timeout = 120;
script.src = jsonpScriptSrc(chunkId);
onScriptComplete = function(event) {
// 处理异常,消除副作用
// …
};
var timeout = setTimeout(function() {
onScriptComplete({ type: ‘timeout’, target: script });
}, 120000);
script.onerror = script.onload = onScriptComplete;
document.head.appendChild(script);
// …
// 动态加载模块
return Promise.all(promises);
};
var jsonpArray = (window[‘webpackJsonp’] = window[‘webpackJsonp’] || []);
// 重写数组 push 方法
jsonpArray.push = webpackJsonpCallback;
jsonpArray = jsonpArray.slice();
for (var i = 0; i < jsonpArray.length; i++)
webpackJsonpCallback(jsonpArray[i]);
return webpack_require((webpack_require.s = 0));
})({
‘./src/index.js’: function(module, exports, webpack_require) {
eval(`
const css = webpack_require.e(0).then(webpack_require.t.bind(null, “./src/style/index.css”, 7))
const a = 100;
console.log(a, css)
`);
},
0: function(module, exports, webpack_require) {
eval(module.exports = __webpack_require__("./src/index.js");
);
}
});
使用 webpack-chain 重写配置
我们用 webpack-chain 来写 webpack 的配置,原因是 webpack-chain 的方式更加灵活
官方解释
webpack-chain
尝试通过提供可链式或顺流式的API
创建和修改webpack
配置。API
的Key
部分可以由用户指定的名称引用,这有助于跨项目修改配置方式的标准化。
const path = require(‘path’);
const rimraf = require(‘rimraf’);
const Config = require(‘webpack-chain’);
const config = new Config();
const resolve = src => {
return path.join(process.cwd(), src);
};
// 删除 dist 目录
rimraf.sync(‘dist’);
config
// 入口
.entry(‘src/index’)
.add(resolve(‘src/index.js’))
.end()
// 模式
// .mode(process.env.NODE_ENV) 等价下面
.set(‘mode’, process.env.NODE_ENV)
// 出口
.output.path(resolve(‘dist’))
.filename(‘[name].bundle.js’);
config.module
.rule(‘css’)
.test(/.css$/)
.use(‘css’)
.loader(‘css-loader’);
module.exports = config.toConfig();
课时 1 小结
至此课时 1 已经结束了,我们主要做了以下事情
-
webpack 基础配置
-
将 css 通过 css-loader 打包进 js 中
-
解析 bundle 如何加载模块的
-
webpack 如何实现的动态加载模块
学习一个工具我们不仅要看懂它的配置,还要对它的原理一起了解,只有学到框架的精髓,我们才能应对如今大前端如此迅猛的发展。
课题 2:搭建开发环境跟生产环境
本章提要:
-
目录
-
实现可插拔配置
-
构建生产环境
-
构建开发环境(devServer)
-
提取 css
-
自动生成 html
-
项目测试
目录
│── build
│ │── base.js // 公共部分
│ │── build.js
│ └── dev.js
│── config
│ │── base.js // 基础配置
│ │── css.js // css 配置
│ │── HtmlWebpackPlugin.js // html 配置
│ └── MiniCssExtractPlugin.js // 提取css
│── public // 公共资源
│ └── index.html // html 模版
└── src // 开发目录
│── style
│ └── index.css
└── main.js // 主入口
实现可插拔配置
package.json
{
“scripts”: {
“dev”: “cross-env NODE_ENV=development node build/dev.js”,
“build”: “cross-env NODE_ENV=production node build/build.js”
},
“dependencies”: {
“cross-env”: “^6.0.3”,
“css-loader”: “^3.2.0”,
“cssnano”: “^4.1.10”,
“ora”: “^4.0.3”,
“rimraf”: “^3.0.0”,
“webpack”: “^4.41.2”
},
“devDependencies”: {
“extract-text-webpack-plugin”: “^3.0.2”,
“html-webpack-plugin”: “^3.2.0”,
“mini-css-extract-plugin”: “^0.8.0”,
“vue-cli-plugin-commitlint”: “^1.0.4”,
“webpack-chain”: “^6.0.0”,
“webpack-cli”: “^3.3.10”,
“webpack-dev-server”: “^3.9.0”
}
}
build/base.js
const { findSync } = require(‘…/lib’);
const Config = require(‘webpack-chain’);
const config = new Config();
const files = findSync(‘config’);
const path = require(‘path’);
const resolve = p => {
return path.join(process.cwd(), p);
};
module.exports = () => {
const map = new Map();
files.map(_ => {
const name = _.split(‘/’)
.pop()
.replace(‘.js’, ‘’);
return map.set(name, require(_)(config, resolve));
});
map.forEach(v => v());
return config;
};
构建生产环境
build/build.js
const rimraf = require(‘rimraf’);
const ora = require(‘ora’);
const chalk = require(‘chalk’);
const path = require(‘path’);
// 删除 dist 目录
rimraf.sync(path.join(process.cwd(), ‘dist’));
const config = require(‘./base’)();
const webpack = require(‘webpack’);
const spinner = ora(‘开始构建项目…’);
spinner.start();
webpack(config.toConfig(), function(err, stats) {
spinner.stop();
if (err) throw err;
process.stdout.write(
stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + ‘\n\n’
);
if (stats.hasErrors()) {
console.log(chalk.red(‘构建失败\n’));
process.exit(1);
}
console.log(chalk.cyan(‘build完成\n’));
});
构建开发环境(devServer)
build/dev.js
const config = require(‘./base’)();
const webpack = require(‘webpack’);
const chalk = require(‘chalk’);
const WebpackDevServer = require(‘webpack-dev-server’);
const port = 8080;
const publicPath = ‘/common/’;
config.devServer
.quiet(true)
.hot(true)
.https(false)
.disableHostCheck(true)
.publicPath(publicPath)
.clientLogLevel(‘none’);
const compiler = webpack(config.toConfig());
// 拿到 devServer 参数
const chainDevServer = compiler.options.devServer;
const server = new WebpackDevServer(
compiler,
Object.assign(chainDevServer, {})
);
[‘SIGINT’, ‘SIGTERM’].forEach(signal => {
process.on(signal, () => {
server.close(() => {
process.exit(0);
});
});
});
// 监听端口
server.listen(port);
new Promise(() => {
compiler.hooks.done.tap(‘dev’, stats => {
const empty = ’ ';
const common = `App running at:
- Local: http://127.0.0.1: p o r t {port} port{publicPath}\n`;
console.log(chalk.cyan(‘\n’ + empty + common));
});
});
提取 css
config/css.js
css 提取 loader 配置
module.exports = (config, resolve) => {
return (lang, test) => {
const baseRule = config.module.rule(lang).test(test);
const normalRule = baseRule.oneOf(‘normal’);
applyLoaders(normalRule);
function applyLoaders(rule) {
rule
.use(‘extract-css-loader’)
.loader(require(‘mini-css-extract-plugin’).loader)
.options({
publicPath: ‘./’
});
rule
.use(‘css-loader’)
.loader(‘css-loader’)
.options({});
}
};
};
css 提取插件 MiniCssExtractPlugin
config/MiniCssExtractPlugin.js
const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’);
module.exports = (config, resolve) => {
return () => {
config
.oneOf(‘normal’)
.plugin(‘mini-css-extract’)
.use(MiniCssExtractPlugin);
};
};
自动生成 html
config/HtmlWebpackPlugin.js
const HtmlWebpackPlugin = require(‘html-webpack-plugin’);
module.exports = (config, resolve) => {
return () => {
config.plugin(‘html’).use(HtmlWebpackPlugin, [
{
template: ‘public/index.html’
}
]);
};
};
项目测试
测试 html 模板
public/index.html
测试 css 模板
src/style/index.css
.test {
width: 200px;
height: 200px;
color: red;
background-color: orange;
}
程序入口
src/main.js
require(‘./style/index.css’);
const h2 = document.createElement(‘h2’);
h2.className = ‘test’;
h2.innerText = ‘test’;
document.body.append(h2);
课题 3:基础配置之loader
本章提要:
-
配置 babel
-
使用 babel 配置 ts
-
ts 静态类型检查
-
友好错误提示插件
-
配置样式,style,css、less、sass、postcss 等
-
postcss 配置
-
编译前后 css 对比
-
配置 autoprefixer
-
开启 source map
目录
增加以下文件
│──── config // 配置目录
│ │── babelLoader.js // babel-loader 配置
│ │── ForkTsChecker.js // ts 静态检查
│ │── FriendlyErrorsWebpackPlugin.js // 友好错误提示
│ └── style
│──── src // 开发目录
│ │── style
│ │ │── app.css
│ │ │── index.less // 测试 less
│ │ │── index.scss // 测试 sass
│ │ └── index.postcss // 测试 postcss
│ └── ts
│ └── index.ts // 测试 ts
│── babel.js
│── postcss.config.js // postcss 配置
│── tsconfig.json // ts 配置
└──── dist // 打包后的目录
│── app.bundle.js
│── app.css
└── index.html
配置 babel
config/babelLoader.js
module.exports = (config, resolve) => {
const baseRule = config.module.rule(‘js’).test(/.js│.tsx?$/);
const babelPath = resolve(‘babel.js’);
const babelConf = require(babelPath);
const version = require(resolve(‘node_modules/@babel/core/package.json’))
.version;
return () => {
baseRule
.use(‘babel’)
.loader(require.resolve(‘babel-loader’))
.options(babelConf({ version }));
};
};
使用 babel 配置 ts
这里我们使用 babel
插件 @babel/preset-typescript
将 ts
转成 js,并使用
ForkTsCheckerWebpackPlugin
、ForkTsCheckerNotifierWebpackPlugin
插件进行错误提示。
babel.js
module.exports = function(api) {
return {
presets: [
[
‘@babel/preset-env’,
{
targets: {
chrome: 59,
edge: 13,
firefox: 50,
safari: 8
}
}
],
[
‘@babel/preset-typescript’,
{
allExtensions: true
}
]
],
plugins: [
‘@babel/plugin-transform-typescript’,
‘transform-class-properties’,
‘@babel/proposal-object-rest-spread’
]
};
};
ts 静态类型检查
const ForkTsCheckerWebpackPlugin = require(‘fork-ts-checker-webpack-plugin’);
const ForkTsCheckerNotifierWebpackPlugin = require(‘fork-ts-checker-notifier-webpack-plugin’);
module.exports = (config, resolve) => {
return () => {
config.plugin(‘ts-fork’).use(ForkTsCheckerWebpackPlugin, [
{
// 将async设为false,可以阻止Webpack的emit以等待类型检查器/linter,并向Webpack的编译添加错误。
async: false
}
]);
// 将TypeScript类型检查错误以弹框提示
// 如果fork-ts-checker-webpack-plugin的async为false时可以不用
// 否则建议使用,以方便发现错误
config.plugin(‘ts-notifier’).use(ForkTsCheckerNotifierWebpackPlugin, [
{
title: ‘TypeScript’,
excludeWarnings: true,
skipSuccessful: true
}
]);
};
};
友好错误提示插件
config/FriendlyErrorsWebpackPlugin.js
const FriendlyErrorsWebpackPlugin = require(‘friendly-errors-webpack-plugin’);
module.exports = (config, resolve) => {
return () => {
config.plugin(‘error’).use(FriendlyErrorsWebpackPlugin);
};
};
配置样式,style,css、less、sass、postcss 等
module.exports = (config, resolve) => {
const createCSSRule = (lang, test, loader, options = {}) => {
const baseRule = config.module.rule(lang).test(test);
const normalRule = baseRule.oneOf(‘normal’);
normalRule
.use(‘extract-css-loader’)
.loader(require(‘mini-css-extract-plugin’).loader)
.options({
hmr: process.env.NODE_ENV === ‘development’,
publicPath: ‘/’
});
normalRule
.use(‘css-loader’)
.loader(require.resolve(‘css-loader’))
.options({});
normalRule.use(‘postcss-loader’).loader(require.resolve(‘postcss-loader’));
if (loader) {
const rs = require.resolve(loader);
normalRule
.use(loader)
.loader(rs)
.options(options);
}
};
return () => {
createCSSRule(‘css’, /.css$/, ‘css-loader’, {});
createCSSRule(‘less’, /.less$/, ‘less-loader’, {});
createCSSRule(‘scss’, /.scss$/, ‘sass-loader’, {});
createCSSRule(‘postcss’, /.p(ost)?css$/);
};
};
postcss 配置
module.exports = {
plugins: {
‘postcss-px-to-viewport’: {
unitToConvert: ‘px’,
viewportWidth: 750,
unitPrecision: 5,
propList: [‘*’],
viewportUnit: ‘vw’,
fontViewportUnit: ‘vw’,
selectorBlackList: [],
minPixelValue: 1,
mediaQuery: false,
replace: true,
exclude: [],
landscape: false,
landscapeUnit: ‘vw’,
landscapeWidth: 568
}
}
};
编译前后 css 对比
src/style/index.less
/* index.less */
.test {
width: 300px;
}
dist/app.css
/* index.css */
.test {
width: 36.66667vw;
height: 26.66667vw;
color: red;
background-color: orange;
}
/* app.css */
.test {
font-size: 8vw;
}
/* index.less */
.test {
width: 40vw;
}
/* index.scss */
.test {
height: 40vw;
}
/* index.postcss */
.test {
background: green;
height: 26.66667vw;
}
配置 autoprefixer
自动添加 css 前缀
postcss.config.js
module.exports = {
plugins: {
autoprefixer: {
overrideBrowserslist: [
‘> 1%’,
‘last 3 versions’,
‘iOS >= 8’,
‘Android >= 4’,
‘Chrome >= 40’
]
}
}
};
转换前
/* index.css */
.test {
width: 200px;
height: 200px;
color: red;
display: flex;
background-color: orange;
}
转换后
/* index.css */
.test {
width: 26.66667vw;
height: 26.66667vw;
color: red;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
background-color: orange;
}
开启 source map
config.devtool(‘cheap-source-map’);
└── dist
│── app.bundle.js
│── app.bundle.js.map
│── app.css
│── app.css.map
└── index.html
在源文件下会有一行注释,证明开启了 sourcemap
/# sourceMappingURL=app.css.map/
课时 4:webpack性能优化
本章讲解
-
分离 Manifest
-
Code Splitting(代码分割)
-
Bundle Splitting(打包分割)
-
Tree Shaking(删除死代码)
-
开启 gzip
分离 Manifest
module.exports = (config, resolve) => {
return () => {
config
.optimization
.runtimeChunk({
name: “manifest”
})
}
}
Code Splitting
-
使用动态 import 或者 require.ensure 语法,在第一节已经讲解
-
使用
babel-plugin-import
插件按需引入一些组件库
Bundle Splitting
将公共的包提取到 chunk-vendors
里面,比如你require(‘vue’),webpack 会将 vue 打包进 chunk-vendors.bundle.js
module.exports = (config, resolve) => {
return () => {
config
.optimization.splitChunks({
chunks: ‘async’,
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 3,
maxInitialRequests: 3,
cacheGroups: {
vendors: {
name: chunk-vendors
,
test: /[\/]node_modules[\/]/,
priority: -10,
chunks: ‘initial’
},
common: {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
最后
给大家分享一些关于HTML的面试题。
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
mization
.runtimeChunk({
name: “manifest”
})
}
}
Code Splitting
-
使用动态 import 或者 require.ensure 语法,在第一节已经讲解
-
使用
babel-plugin-import
插件按需引入一些组件库
Bundle Splitting
将公共的包提取到 chunk-vendors
里面,比如你require(‘vue’),webpack 会将 vue 打包进 chunk-vendors.bundle.js
module.exports = (config, resolve) => {
return () => {
config
.optimization.splitChunks({
chunks: ‘async’,
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 3,
maxInitialRequests: 3,
cacheGroups: {
vendors: {
name: chunk-vendors
,
test: /[\/]node_modules[\/]/,
priority: -10,
chunks: ‘initial’
},
common: {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-ntMvMLKt-1712460318542)]
[外链图片转存中…(img-OTWMJd3P-1712460318544)]
[外链图片转存中…(img-j5U3lRmi-1712460318544)]
[外链图片转存中…(img-6r0yyAEH-1712460318545)]
[外链图片转存中…(img-Gfyizs6k-1712460318545)]
[外链图片转存中…(img-KEBStLJu-1712460318546)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-68zxgaOJ-1712460318546)]
最后
给大家分享一些关于HTML的面试题。
[外链图片转存中…(img-Eg1gcDVk-1712460318546)]
[外链图片转存中…(img-0hb2hNfU-1712460318547)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-7AgPjNOg-1712460318547)]