web浏览器中的javascript
- 客户端javascript
- 在html里嵌入javascript
- javascript程序的执行
- 兼容性和互用性
- 可访问性
- 安全性
- 客户端框架
- 开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
window对象
-
计时器
-
浏览器定位和导航
-
浏览历史
-
浏览器和屏幕信息
-
对话框
-
错误处理
-
作为window对象属性的文档元素
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: {
name: chunk-common
,
minChunks: 2,
priority: -20,
chunks: ‘initial’,
reuseExistingChunk: true
}
}
})
config.optimization.usedExports(true)
}
}
Tree Shaking
config/optimization.js
config.optimization.usedExports(true);
src/treeShaking.js
export function square(x) {
return x * x;
}
export function cube(x) {
return x * x * x;
}
在 main.js 中只引用了 cube
import { cube } from ‘./treeShaking’;
console.log(cube(2));
未使用 Tree Shaking
{
“./src/treeShaking.js”: function(
module,
webpack_exports,
webpack_require
) {
“use strict”;
webpack_require.r(webpack_exports);
webpack_require.d(webpack_exports, “square”, function() {
return square;
});
webpack_require.d(webpack_exports, “cube”, function() {
return cube;
});
function square(x) {
return x * x;
}
function cube(x) {
return x * x * x;
}
}
}
使用了 Tree Shaking
这里只导出了 cube 函数,并没有将 square 导出去
当然你可以看见 square 函数还是在 bundle 里面,但是在压缩的时候就会被干掉了,因为它并没有被引用
{
“./src/treeShaking.js”: function(
module,
webpack_exports,
webpack_require
) {
“use strict”;
webpack_require.d(webpack_exports, “a”, function() {
return cube;
});
function square(x) {
return x * x;
}
function cube(x) {
return x * x * x;
}
}
}
只有当函数给定输入后,产生相应的输出,且不修改任何外部的东西,才可以安全做shaking的操作
如何使用tree-shaking?
-
确保代码是es6格式,即 export,import
-
package.json中,设置 sideEffects
-
确保 tree-shaking 的函数没有副作用
-
babelrc中设置presets [[“@babel/preset-env”, { “modules”: false }]] 禁止转换模块,交由webpack进行模块化处理
-
结合uglifyjs-webpack-plugin
其实在 webpack4
我们根本不需要做这些操作了,因为 webpack
在生产环境已经帮我们默认添加好了,开箱即用!
开启 gzip
CompressionWebpackPlugin.js
const CompressionWebpackPlugin = require(‘compression-webpack-plugin’);
module.exports = (config, resolve) => {
return () => {
config.plugin(‘CompressionWebpackPlugin’).use(CompressionWebpackPlugin, [
{
algorithm: ‘gzip’,
test: /.js(?.*)?$/i,
threshold: 10240,
minRatio: 0.8
}
]);
};
};
课时 5:手写loader实现可选链
本章内容
-
什么是 webpack loader
-
可选链介绍
-
loader 实现可选链
什么是 webpack loader
webpack loader
是 webpack
为了处理各种类型文件的一个中间层,webpack
本质上就是一个 node
模块,它不能处理 js
以外的文件,那么 loader
就帮助 webpack
做了一层转换,将所有文件都转成字符串,你可以对字符串进行任意操作/修改,然后返回给 webpack
一个包含这个字符串的对象,让 webpack
进行后面的处理。如果把 webpack
当成一个垃圾工厂的话,那么 loader
就是这个工厂的垃圾分类!
可选链介绍
这里并不是纯粹意义上的可选链,因为 babel
跟 ts
都已经支持了,我们也没有必要去写一个完整的可选链,只是来加深一下对 loader
的理解, loader
在工作当中能帮助我们做什么?
用途
当我们访问一个对象属性时不必担心这个对象是 undefined
而报错,导致程序不能继续向下执行
解释
在 ?
之前的所有访问链路都是合法的,不会产生报错
const obj = {
foo: {
bar: {
baz: 2
}
}
}
console.log(obj.foo.bar?.baz) // 2
// 被转成 obj && obj.foo && obj.foo.bar && obj.foo.bar.baz
console.log(obj.foo.err?.baz) // undefined
// 被转成 obj && obj.foo && obj.foo.err && obj.foo.err.baz
loader 实现可选链
配置loader,options-chain-loader
config/OptionsChainLoader.js
module.exports = (config, resolve) => {
const baseRule = config.module.rule(‘js’).test(/.js|.tsx?$/);
const normalRule = baseRule.oneOf(‘normal’);
return () => {
normalRule
.use(‘options-chain’)
.loader(resolve(‘options-chain-loader’))
}
}
其实就是正则替换,loader
将整个文件全部转换成字符串,content
就是整个文件的内容,对 content
进行修改,修改完成后再返回一个新的 content
就完成了一个 loader
转换。是不是很简单?
下面的操作意思就是,我们匹配 obj.foo.bar?.
并把它转成 obj && obj.foo && obj.foo.bar && obj.foo.bar.
options-chain-loader.js
module.exports = function(content) {
return content.replace(new RegExp(/([$_\w.]+?.)/,‘g’),function(res) {
let str = res.replace(/?./,‘’);
let arrs = str.split(‘.’);
let strArr = [];
for(let i = 1; i <= arrs.length; i++) {
strArr.push(arrs.slice(0,i).join(‘.’));
}
let compile = strArr.join(‘&&’);
const done = compile + ‘&&’ + str + ‘.’
return done;
});
};
课时 6:webpack编译优化
本章内容
-
cache-loader
-
DllPlugin
-
threadLoader
cache-loader
cache-loader
主要是将打包好的文件缓存在硬盘的一个目录里,一般存在 node_modules/.cache
下,当你再次 build
的时候如果此文件没有修改就会从缓存中读取已经编译过的文件,只有有改动的才会被编译,这样就大大降低了编译的时间。尤其是项目越大时越明显。
此项目使用前后数据对比 3342ms --> 2432ms 效果还是比较明显
这里只对 babel 加入了 cache-loader,因为我们的 ts/js 都是由 babel 进行编译的,不需要对 ts-loader 缓存(我们也没有用到)
config/cacheLoader.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
.exclude
.add(filepath => {
// 不缓存 node_modules 下的文件
return /node_modules/.test(filepath)
})
.end()
.use(‘cache-loader’)
.loader(‘cache-loader’)
.options({
// 缓存位置
cacheDirectory: resolve(‘node_modules/.cache/babel’)
})
}
}
DllPlugin
DllPlugin 是将第三方长期不变的包与实际项目隔离开来并分别打包,当我们 build 时再将已经打包好的 dll 包引进来就 ok 了
我提取了两个包 vue、react,速度差不多提升了 200ms,从 2698ms 到 2377ms
打包 dll
build/dll.js
const path = require(“path”);
const dllPath = path.join(process.cwd(), ‘dll’);
const Config = require(‘webpack-chain’);
const config = new Config();
const webpack = require(‘webpack’)
const rimraf = require(‘rimraf’);
const ora = require(‘ora’)
const chalk = require(‘chalk’)
const BundleAnalyzerPlugin = require(‘…/config/BundleAnalyzerPlugin’)(config)
BundleAnalyzerPlugin()
config
.entry(‘dll’)
.add(‘vue’)
.add(‘react’)
.end()
.set(‘mode’, “production”)
.output
.path(dllPath)
.filename(‘[name].js’)
.library(“[name]”)
.end()
.plugin(‘DllPlugin’)
.use(webpack.DllPlugin, [{
name: “[name]”,
path: path.join(process.cwd(), ‘dll’, ‘manifest.json’),
}])
.end()
rimraf.sync(path.join(process.cwd(), ‘dll’))
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’))
})
将 dll 包合并
const webpack = require(‘webpack’)
module.exports = (config, resolve) => {
return () => {
config.plugin(‘DllPlugin’)
.use(webpack.DllReferencePlugin, [{
context: process.cwd(),
manifest: require(resolve(‘dll/manifest.json’))
}])
}
}
threadLoader
测试效果变差了 ???,线程数越小编译速度越快
config/threadLoader.js
module.exports = (config, resolve) => {
const baseRule = config.module.rule(‘js’).test(/.js|.tsx?$/);
return () => {
const useThreads = true;
if (useThreads) {
const threadLoaderConfig = baseRule
.use(‘thread-loader’)
.loader(‘thread-loader’);
threadLoaderConfig.options({ workers: 3 })
}
}
}
课时 7:多页面配置
注意
-
弃用
npm run build & npm run dev & npm run dll -
改成
box build & box dev & box dll -
link
npm link 将 box 命令链接到全局
本章内容
-
使用
-
改造为脚手架
-
多页面配置
使用
box build # 不加参数则会编译所有页面,并清空 dist
box dev # 默认编译 index 页面
参数
index2 是指定编译的页面。不会清空 dist
report 开启打包分析
box build index2 --report
box dev index2 --report
改造为脚手架
分成三个命令,进行不同操作
-
build
-
dev
-
dll
bin/box.js
#!/usr/bin/env node
const chalk = require(‘chalk’)
const program = require(‘commander’)
const packageConfig = require(‘…/package.json’);
const { cleanArgs } = require(‘…/lib’)
const path = require(‘path’)
const name = build,dev,dll
let boxConf = {}
let lock = false
try {
boxConf = require(path.join(process.cwd(), ‘box.config.js’))()
} catch (error) { }
program
.usage(‘ [options]’)
.version(packageConfig.version)
.command(‘build [app-page]’)
.description(构建开发环境
)
.option(‘-r, --report’, ‘打包分析报告’)
.option(‘-d, --dll’, ‘合并差分包’)
.action(async (name, cmd) => {
const options = cleanArgs(cmd)
const args = Object.assign(options, { name }, boxConf)
if (lock) return
lock = true;
if (boxConf.pages) {
Object.keys(boxConf.pages).forEach(page => {
args.name = page;
require(‘…/build/build’)(args)
})
} else {
require(‘…/build/build’)(args)
}
})
program
.usage(‘ [options]’)
.version(packageConfig.version)
.command(‘dev [app-page]’)
.description(构建生产环境
)
.option(‘-d, --dll’, ‘合并差分包’)
.action(async (name, cmd) => {
const options = cleanArgs(cmd)
const args = Object.assign(options, { name }, boxConf)
if (lock) return
lock = true;
require(‘…/build/dev’)(args)
})
program
.usage(‘ [options]’)
.version(packageConfig.version)
.command(‘dll [app-page]’)
.description(编译差分包
)
.action(async (name, cmd) => {
const options = cleanArgs(cmd)
const args = Object.assign(options, { name }, boxConf)
if (lock) return
lock = true;
require(‘…/build/dll’)(args)
})
program.parse(process.argv).args && program.parse(process.argv).args[0];
program.commands.forEach(c => c.on(‘–help’, () => console.log()))
if (process.argv[2] && !name.includes(process.argv[2])) {
console.log()
console.log(chalk.red( 没有找到 ${process.argv[2]} 命令
))
console.log()
program.help()
}
if (!process.argv[2]) {
program.help()
}
多页面配置
box.config.js
module.exports = function (config) {
return {
entry: ‘src/main.js’, // 默认入口
dist: ‘dist’, // 默认打包目录
publicPath: ‘/’,
port: 8888,
pages: {
index: {
entry: ‘src/main.js’,
template: ‘public/index.html’,
filename: ‘index.html’,
},
index2: {
entry: ‘src/main.js’,
template: ‘public/index2.html’,
filename: ‘index2.html’,
}
},
chainWebpack(config) {
}
}
}
课时 8:手写一个webpack插件
如果把 webpack 当成一个垃圾工厂,loader 就是垃圾分类,将所有垃圾整理好交给 webpack。plugin 就是如何去处理这些垃圾。
webpack 插件写起来很简单,就是你要知道各种各样的钩子在什么时候触发,然后你的逻辑写在钩子里面就ok了
-
apply
函数是 webpack 在调用 plugin 的时候执行的,你可以认为它是入口 -
compiler
暴露了和 webpack 整个生命周期相关的钩子 -
Compilation
暴露了与模块和依赖有关的粒度更小的事件钩子
本节概要
-
实现一个 CopyPlugin
-
使用
实现一个 CopyPlugin
我们今天写一个 copy 的插件,在webpack构建完成之后,将目标目录下的文件 copy 到另一个目录下
const fs = require(‘fs-extra’)
const globby = require(‘globby’)
class CopyDirWebpackPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
const opt = this.options
compiler.plugin(‘done’, (stats) => {
if (process.env.NODE_ENV === ‘production’) {
(async ()=>{
const toFilesPath = await globby([${opt.to}/**
, ‘!.git/**’])
toFilesPath.forEach(filePath => fs.removeSync(filePath))
const fromFilesPath = await globby([${opt.from}/**
])
fromFilesPath.forEach(fromPath => {
const cachePath = fromPath
fromPath = fromPath.replace(‘dist’, opt.to)
const dirpaths = fromPath.substring(0, fromPath.lastIndexOf(‘/’))
fs.mkdirpSync(dirpaths)
fs.copySync(cachePath, fromPath)
})
console.log( 完成copy ${opt.from} to ${opt.to}
)
})()
}
});
}
}
module.exports = CopyDirWebpackPlugin
使用
将打包出来的 dist 目录下的内容 copy 到 dist2 目录下
const CopyPlugin = require(‘…/webapck-plugin-copy’);
module.exports = ({ config }) => {
return () => {
config.plugin(‘copy-dist’)
.use(CopyPlugin, [{
from: ‘dist’,
to: ‘dist2’
}])
}
}
课时 9:构建 ssr
ssr 就是服务端渲染,做 ssr 的好处就是为了处理 spa 的不足,比如 seo 优化,服务端缓存等问题。
今天主要用 react 的 ssr 来做一个简单的实例,让大家更清晰的入门
本章概要
-
创建 box build:ssr
-
编译 ssr
-
编译 jsx 语法
数据结构与算法
这一块在笔试、面试的代码题中考核较多,其中常考的数据结构主要有:数组、链表、队列、栈、Set、Map、哈希表等,不同数据结构有不同的方法以及储存原理,这些算是技术岗的必备知识。算法部分主要分为两大块,排序算法与一些其他算法题。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
排序算法根据考频高低主要有:快速排序、归并排序、堆排序、冒泡排序、插入排序、选择排序、希尔排序、桶排序、基数排序、Timsort这十种,这类考核点要么是算法的时间、空间复杂度、稳定度,要么是直接手写代码,故在理解算法原理的同时,对JS语言版的排序算法代码也要加强记忆。
- 二叉树层序遍历
- B 树的特性,B 树和 B+树的区别
- 尾递归
- 如何写一个大数阶乘?递归的方法会出现什么问题?
- 把多维数组变成一维数组的方法
- 知道的排序算法 说一下冒泡快排的原理
- Heap 排序方法的原理?复杂度?
- 几种常见的排序算法,手写
- 数组的去重,尽可能写出多个方法
- 如果有一个大的数组,都是整型,怎么找出最大的前 10 个数
- 知道数据结构里面的常见的数据结构
- 找出数组中第 k 大的数组出现多少次,比如数组【1,2, 4,4,3,5】第二大的数字是 4,出现两次,所以返回 2
- 合并两个有序数组
- 给一个数,去一个已经排好序的数组中寻找这个数的位 置(通过快速查找,二分查找)
些垃圾。
webpack 插件写起来很简单,就是你要知道各种各样的钩子在什么时候触发,然后你的逻辑写在钩子里面就ok了
-
apply
函数是 webpack 在调用 plugin 的时候执行的,你可以认为它是入口 -
compiler
暴露了和 webpack 整个生命周期相关的钩子 -
Compilation
暴露了与模块和依赖有关的粒度更小的事件钩子
本节概要
-
实现一个 CopyPlugin
-
使用
实现一个 CopyPlugin
我们今天写一个 copy 的插件,在webpack构建完成之后,将目标目录下的文件 copy 到另一个目录下
const fs = require(‘fs-extra’)
const globby = require(‘globby’)
class CopyDirWebpackPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
const opt = this.options
compiler.plugin(‘done’, (stats) => {
if (process.env.NODE_ENV === ‘production’) {
(async ()=>{
const toFilesPath = await globby([${opt.to}/**
, ‘!.git/**’])
toFilesPath.forEach(filePath => fs.removeSync(filePath))
const fromFilesPath = await globby([${opt.from}/**
])
fromFilesPath.forEach(fromPath => {
const cachePath = fromPath
fromPath = fromPath.replace(‘dist’, opt.to)
const dirpaths = fromPath.substring(0, fromPath.lastIndexOf(‘/’))
fs.mkdirpSync(dirpaths)
fs.copySync(cachePath, fromPath)
})
console.log( 完成copy ${opt.from} to ${opt.to}
)
})()
}
});
}
}
module.exports = CopyDirWebpackPlugin
使用
将打包出来的 dist 目录下的内容 copy 到 dist2 目录下
const CopyPlugin = require(‘…/webapck-plugin-copy’);
module.exports = ({ config }) => {
return () => {
config.plugin(‘copy-dist’)
.use(CopyPlugin, [{
from: ‘dist’,
to: ‘dist2’
}])
}
}
课时 9:构建 ssr
ssr 就是服务端渲染,做 ssr 的好处就是为了处理 spa 的不足,比如 seo 优化,服务端缓存等问题。
今天主要用 react 的 ssr 来做一个简单的实例,让大家更清晰的入门
本章概要
-
创建 box build:ssr
-
编译 ssr
-
编译 jsx 语法
数据结构与算法
这一块在笔试、面试的代码题中考核较多,其中常考的数据结构主要有:数组、链表、队列、栈、Set、Map、哈希表等,不同数据结构有不同的方法以及储存原理,这些算是技术岗的必备知识。算法部分主要分为两大块,排序算法与一些其他算法题。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
排序算法根据考频高低主要有:快速排序、归并排序、堆排序、冒泡排序、插入排序、选择排序、希尔排序、桶排序、基数排序、Timsort这十种,这类考核点要么是算法的时间、空间复杂度、稳定度,要么是直接手写代码,故在理解算法原理的同时,对JS语言版的排序算法代码也要加强记忆。
- 二叉树层序遍历
- B 树的特性,B 树和 B+树的区别
- 尾递归
- 如何写一个大数阶乘?递归的方法会出现什么问题?
- 把多维数组变成一维数组的方法
- 知道的排序算法 说一下冒泡快排的原理
- Heap 排序方法的原理?复杂度?
- 几种常见的排序算法,手写
- 数组的去重,尽可能写出多个方法
- 如果有一个大的数组,都是整型,怎么找出最大的前 10 个数
- 知道数据结构里面的常见的数据结构
- 找出数组中第 k 大的数组出现多少次,比如数组【1,2, 4,4,3,5】第二大的数字是 4,出现两次,所以返回 2
- 合并两个有序数组
- 给一个数,去一个已经排好序的数组中寻找这个数的位 置(通过快速查找,二分查找)
[外链图片转存中…(img-Axe1nPmu-1715599220044)]