本文为拉勾网大前端高薪训练营第一期心得和笔记
经过这段时间拉勾网大前端高薪训练营的学习,进步还是挺明显的,主要是前端这个行业细节太丰富,经常是用到什么查什么,并没法系统地学习前端,自己琢磨会遗漏很多细节,还有很多小技巧和好用的工具也是会错过的。
拉勾网的大前端训练营的视频讲解很详细,视频分成很多小节,每个视频平均5分钟左右,少数复杂的15分钟,比较方便碎片时间学习,当然还是比较推荐边学边记笔记,不然学完就忘了。学习计划也是循序渐进,不过还是需要有些前端基础学习效果最好。视频的代码也会在代码库里更新,节省了自己敲打的时间,视频也是2年有效,随时可以回来翻看。每一个小章节后有问卷作业,每一个大章节后有一次问答题和代码题的作业,每一个大章节之后会有一次串讲的直播,赶不上可以看录播,总的来说学习体验还是挺好的。
班主任很尽责,会通知大家直播时间(催促大家写作业,笑),学起来挺有紧迫感的(杜绝了steam买游戏不玩的,买了教程不学的情况),另外有两个微信群(摸鱼和学习),班主任经常在摸鱼群里发励志文章和表情包,鼓励大家努力学习:D。
有什么疑问可以问老师和同学,包括讲课的老师和批改作业的老师都在微信群里,很方便。
答疑不仅是课程里,也可以是工作学习中遇到的问题,这相当于还增进了人脉,一举两得。说不准学完了得到个内推什么的,那就更赚了。
自动严格模式
单独私有作用域
通过CORS的方式请求外部JS模块,且必须http这种形式,不能通过文件
等同于defer,延迟执行脚本
es module import和export都可以用as重命名
import和export是传引用(变量地址),而不是传值
import的不能改,是const的
import a from 'a.js’不能省略.js
import b from ‘./b/index.js’ 不能省略index.js
import 'modules.js’会认为是外部模块
'./modules.js‘
'/04-import/modules.js'
'http://localhost:3001/04-import/modules.js'
等效
import(importPath).then((module)=>{
console.log(module)
})
script nomodule,只有在不支持module的浏览器才运行脚本,可以用来阻止Polyfill在支持module的浏览器里执行第二次脚本
nodejs支持module,需要后缀改成mjs,执行时
node --experimental-modules index.mjs
ESModule这种方式,系统api支持import {writeFileSync} from ‘fs’,lodash这种第三方的不支持这样使用
ESM里不能使用require module exports __filename __dirname这些commonjs内置的模块
替代做法
// 通过 url 模块的 fileURLToPath 方法转换为路径
import { fileURLToPath } from 'url'
import { dirname } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
console.log(__filename)
console.log(__dirname)
node 12.10版本可以在package.json里增加"type": “module”
此时commonjs的文件需要改成cjs后缀
babel-node index.js --presets=@babel/preset-env
//或者创建.babelrc
{
"presets": ["@babel/preset-env"]
}
//或者选择只用某个插件
{
"plugins": [
"@babel/plugin-transform-modules-commonjs"
]
}
好文章收藏
https://segmentfault.com/a/1190000019890322
https://blog.csdn.net/qq_36380426/article/details/106894870
webpack4 零配置的话会打包src/index.js到dist/main.js
三种mode production development none
小技巧vs code: cmd+k cmd+0折叠所有代码
rules里的use里的loader执行顺序是从后向前
config里的publicPath告诉webpack文件路径
config里limit可以指定文件大小是否使用loader limit: 10*1024就是10KB
资源加载支持
{
test: /.html$/,
use: {
loader: 'html-loader'
options: {
attrs: ['img:src', 'a:href']
}
}
}
const marked = require('marked')
module.exports = source => {
// console.log(source)
// return 'console.log("hello ~")'
const html = marked(source)
// return html
// return `module.exports = "${html}"`
// return `export default ${JSON.stringify(html)}`
// 返回 html 字符串交给下一个 loader 处理
return html
}
返回导出的字符串,然后webpack会执行拼接进js代码里
clean-webpack-plugin 自动删除生成的文件
html-webpack-plugin 生成一个使用bundle.js的html
new HtmlWebpackPlugin({
title: 'Webpack Plugin Sample',
meta: {
viewport: 'width=device-width'
},
template: './src/index.html'
}),
// 用于生成 about.html
new HtmlWebpackPlugin({
filename: 'about.html'
})
copy-webpack-plugin
new CopyWebpackPlugin([
// 'public/**'
'public'
])
这个例子是删除bundle.js里所有的注释,
插件是通过在生命周期的钩子中挂载函数实现扩展
class MyPlugin {
apply (compiler) {
console.log('MyPlugin 启动')
compiler.hooks.emit.tap('MyPlugin', compilation => {
// compilation => 可以理解为此次打包的上下文
for (const name in compilation.assets) {
// console.log(name)
// console.log(compilation.assets[name].source())
if (name.endsWith('.js')) {
const contents = compilation.assets[name].source()
const withoutComments = contents.replace(/\/\*\*+\*\//g, '')
compilation.assets[name] = {
source: () => withoutComments,
size: () => withoutComments.length
}
}
}
})
}
}
方法1 不推荐
webpack —watch
browser-sync dist —files “**/*”
方法2
webpack-dev-server —open
这个不会写磁盘,打包好的是存在内存里,开发会快
只有webpack打包的东西才能被dev-server访问到
webpack config,加了这个dev-server就能访问到了
devServer: {
contentBase: './public',
}
proxy: {
'/api': {
// http://localhost:8080/api/users -> https://api.github.com/api/users
target: 'https://api.github.com',
// http://localhost:8080/api/users -> https://api.github.com/users
pathRewrite: {
'^/api': ''
},
// 不能使用 localhost:8080 作为请求 GitHub 的主机名
changeOrigin: true
}
}
'eval',
代码在eval里执行,只能看到哪个文件,不能看到行列
'cheap-eval-source-map',
代码在eval里执行,只能看到行,没有列,代码是es6转换成es5的
'cheap-module-eval-source-map',
代码在eval里执行,只能看到行,没有列,代码是原本一样的,没有经过loader加工
'eval-source-map',
代码在eval执行,能看到错误的行,列
'cheap-source-map',
'cheap-module-source-map',
'inline-cheap-source-map',
'inline-cheap-module-source-map',
'source-map',
'inline-source-map',
source map是用inline dataurl的方式存在文件里,而不是单独.map文件
'hidden-source-map',
在js里不引用source map,但是生成了source map
'nosources-source-map'
没有源代码,但是提供错误的行列信息
规律就是
开发环境sourcemap推荐cheap-module-source-map
理由
生产环境sourcemap:none
如果实在要在生产环境用sourcemap,用nosources-source-map,只会报错的行列,不会暴露源代码
webpack-dev-server —hot
或者config里开启
const webpack = require('webpack')
devServer: {
hot: true
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
HMR对于css可以开箱即用,但是js不行,因为js文件导出没有规律,需要自己制定HMR API
//手动写热更新的API
if (module.hot) {
let lastEditor = editor
module.hot.accept('./editor', () => {
// console.log('editor 模块更新了,需要这里手动处理热替换逻辑')
// console.log(createEditor)
const value = lastEditor.innerHTML
document.body.removeChild(lastEditor)
const newEditor = createEditor()
newEditor.innerHTML = value
document.body.appendChild(newEditor)
lastEditor = newEditor
})
module.hot.accept('./better.png', () => {
img.src = background
console.log(background)
})
}
HMR问题
HMR API代码有错误,更新代码后会自动刷新导致看不到报错信息
解决方法:hotOnly: true
判断是否存在module.hot,再执行
3.代码中多了一些与业务无关的代码,不会打包到最后的代码里
const merge = require('webpack-merge')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const common = require('./webpack.common')
module.exports = merge(common, {
mode: 'production',
plugins: [
new CleanWebpackPlugin(),
new CopyWebpackPlugin(['public'])
]
})
webpack prod会默认开启DefinePlugin,注入全局变量process.env.NODE_ENV
手动注入的时候注意,会替换这个变量为你写的字符串,所以需要提前JSON.stringify()
prod会自动开启tree-shaking,dead code比如return后的代码,和未使用到的函数等代码都不会打包到最后js里
手动开启tree shaking
module.exports = {
mode: 'none',
entry: './src/index.js',
output: {
filename: 'bundle.js'
},
optimization: {
// 模块只导出被使用的成员,相当于标记枯树枝
usedExports: true,
// 尽可能合并每一个模块到一个函数中,scope hoisting作用域提升
concatenateModules: true,
// 压缩输出结果,删除未使用的,摇掉枯树枝
// minimize: true
}
}
babel-loader默认保留ESM,因此tree shaking还能正常工作,tree shaking必须要基于ESM模块才行,如果要强行commonjs模块,配置是是数组套数组
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
// 如果 Babel 加载模块时已经转换了 ESM,则会导致 Tree Shaking 失效
// ['@babel/preset-env', { modules: 'commonjs' }]
// ['@babel/preset-env', { modules: false }]
// 也可以使用默认配置,也就是 auto,这样 babel-loader 会自动关闭 ESM 转换
['@babel/preset-env', { modules: 'auto' }]
]
}
}
}
]
prod会自动开启sideEffects
sideEffects用来标记不用的代码删除是否有副作用,需要在告诉webpack,没有用到的模块不会打包
手动开启的时候会做两个事情,
optimization: {sideEffects: true}
package.json里增加sideEffects: false,
挂载在prototype上的method,还有css都算是sideEffects,要么彻底关闭,要么告诉webpack哪些文件是有副作用的,不要删除这些里面的代码,做法是在package.json里
sideEffects: [
"./src/extend.js",
"*.css"
]
两种方式
多入口entry需要是{},如果是[]的话就是多文件打包在一起
注意output和HtmlWebpackPlugin的配置chunks
entry: {
index: './src/index.js',
album: './src/album.js'
},
output: {
filename: '[name].bundle.js'
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Multi Entry',
template: './src/index.html',
filename: 'index.html',
chunks: ['index']
}),
new HtmlWebpackPlugin({
title: 'Multi Entry',
template: './src/album.html',
filename: 'album.html',
chunks: ['album']
})
]
optimization: {
splitChunks: {
// 自动提取所有公共模块到单独 bundle
chunks: 'all'
}
},
//比如例子里会生成album-index.bundle.js
在js代码里用esm的import then的方式来加载模块
如果是react或vue的话,建议在路由部分动态加载import then
一般这样生成的文件是1.bundle.js, 2.bundle.js
如果要给文件起名,就如下webpackChunkName: ‘name’ 魔法注释,告诉webpack文件名
if (hash === '#posts') {
// mainElement.appendChild(posts())
import(/* webpackChunkName: 'components' */'./posts/posts').then(({ default: posts }) => {
mainElement.appendChild(posts())
})
} else if (hash === '#album') {
// mainElement.appendChild(album())
import(/* webpackChunkName: 'components' */'./album/album').then(({ default: album }) => {
mainElement.appendChild(album())
})
}
建议超过150KB的话才按需加载css,注意不要使用style-loader,这是把css inline到js的loader
OptimizeCssAssetsWebpackPlugin压缩css,这个插件可以放在Plugins里,但是更推荐放在optimization的minimizer里,这样dev时就不开启,prod就开启
有个副作用,就是如果配置了optimization minimizer,默认的js压缩器就被覆盖了,所以还得手动写进去TerserWebpackPlugin用来压缩js文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')
optimization: {
minimizer: [
new TerserWebpackPlugin(),
new OptimizeCssAssetsWebpackPlugin()
]
},
module: {
rules: [
{
test: /\.css$/,
use: [
// 'style-loader', // 将样式通过 style 标签注入
MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Dynamic import',
template: './src/index.html',
filename: 'index.html'
}),
new MiniCssExtractPlugin()
]
用来解决浏览器缓存过长无法更新文件,过短缓存效果不好的问题
凡是文件名的地方都支持占位符来实现hash
三种方式
[hash] 代码任何地方改动,都会引起所有的文件名变化
[chunkhash] chunk代码改动才会引起同一个chunk的文件名变化
[contenthash] 文件级别的hash,只会根据当前文件生成hash
[contenthash:8]可以控制hash数字位数
控制缓存推荐contenthash:8
output: {
filename: '[name]-[hash].bundle.js'
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Dynamic import',
template: './src/index.html',
filename: 'index.html'
}),
new MiniCssExtractPlugin({
filename: '[name]-[contenthash:8].bundle.css'
})
]
自动会开启tree-shaking
yarn rollup ./src/index.js --format iife --file dist/bundle.js
yarn rollup --config
rollup.config.js
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife'
}
}
webpack三种扩展方式loader plugin minimizer
rollup只有plugin
import json from 'rollup-plugin-json'
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife'
},
plugins: [
json()
]
}
rollup不能直接像webpack一样直接import名称导入nodemodules的模块
需要只用rollup-plugin-node-resolve
rollup默认只能处理es版本的模块,比如lodash-es,如果要使用普通的模块,需要roll-plugin-commonjs
rollup多入口可以是input写[‘foo.js’, ‘bar.js’],也可以是{foo: ‘foo.js’, bar: ‘bar.js’}
rollup代码拆分,动态导入import(’’).then
代码拆分必须使用amd或者commonjs,不能用umd或者IIFE,浏览器只能用amd
如果html要加载amd代码不能直接引用,需要requirejs
format: amd umd commjs iife
如果是开发一个类库/框架,就可以使用rollup,如果是开发webapp,webpack更好,因为引用第三方库,HMR都很好用
yarn add parcel-bundler —dev
yarn parcel src/index.html
会打包并自动server运行html,支持模块热替换
parcel hmr api只有一个参数
if (module.hot) {
module.hot.accept(() => {
console.log('hmr')
})
}
parcel自动安装库的依赖
比如开发过程中加入jquery,不用停止server
也支持拆分代码,用动态引入, import then
parcel prod
yarn parcel build src/index.html
parcel打包速度比webpack快,因为使用多进程同时工作,发挥多核cpu性能
webpack也可以用happypack的插件来实现多进程工作
yarn add eslint --dev
yarn eslint --init
//popular format: airbnb standard google;其中standard不需要后面加分号
yarn eslint ./index.js
yarn eslint ./index.js --fix //自动修复
env列表
browser
- browser global variables.node
- Node.js global variables and Node.js scoping.commonjs
- CommonJS global variables and CommonJS scoping (use this for browser-only code that uses Browserify/WebPack).shared-node-browser
- Globals common to both Node.js and Browser.es6
- enable all ECMAScript 6 features except for modules (this automatically sets the ecmaVersion
parser option to 6).es2017
- adds all ECMAScript 2017 globals and automatically sets the ecmaVersion
parser option to 8.es2020
- adds all ECMAScript 2020 globals and automatically sets the ecmaVersion
parser option to 11.worker
- web workers global variables.amd
- defines require()
and define()
as global variables as per the amd spec.mocha
- adds all of the Mocha testing global variables.jasmine
- adds all of the Jasmine testing global variables for version 1.3 and 2.0.jest
- Jest global variables.phantomjs
- PhantomJS global variables.protractor
- Protractor global variables.qunit
- QUnit global variables.jquery
- jQuery global variables.prototypejs
- Prototype.js global variables.shelljs
- ShellJS global variables.meteor
- Meteor global variables.mongo
- MongoDB global variables.applescript
- AppleScript global variables.nashorn
- Java 8 Nashorn global variables.serviceworker
- Service Worker global variables.atomtest
- Atom test helper globals.embertest
- Ember test helper globals.webextensions
- WebExtensions globals.greasemonkey
- GreaseMonkey globals..eslintrc例子
module.exports = {
env: {
browser: false,
es6: false
},
extends: [
'standard'
],
parserOptions: {
ecmaVersion: 2015
},
rules: {
'no-alert': "error"
},
globals: {
"jQuery": "readonly"
}
}
用注释禁用某一行eslint检查
const str1 = "${name} is a coder" // eslint-disable-line no-template-curly-in-string
const script = () => {
return src('src/assets/scripts/*.js', {base: 'src'})
.pipe(plugins.eslint())
.pipe(plugins.eslint.format()) //打印错误
.pipe(plugins.eslint.failAfterError()) //有错误停止执行
.pipe(plugins.babel({presets: ['@babel/preset-env']}))
.pipe(dest('temp'))
.pipe(bs.reload({stream: true}))
}
{
test: /\.js$/,
exclude: /node_modules/,
use: 'eslint-loader',
enforce: 'pre //让这个rule提前执行
}
yarn add eslint-plugin-react --dev
rules: [
'react/jsx-uses-react': 2, //2 === error
'react/jsx-uses-vars': 2
],
plugins: [
'react'
]
//.eslintrc.js
yarn eslint --init
//选择typescript yes
parser: '@typescript-eslint/parser'
plugins: ['@typescript-eslint']
yarn add stylelint --dev
//.stylelintrc.js
module.exports = [
extends: [
'stylelint-config-standard',
'stylelint-config-sass-guidelines'
]
]
//yarn add stylelint-config-standard --dev
yarn stylelint ./index.css
yarn add stylelint-config-sass-guidelines --dev
yarn add prettier --dev
yarn prettier style.css --write//不加--write会打印出来
yarn prettier . --write//把根目录的文件都改了
https://zhuanlan.zhihu.com/p/27094880
yarn add husky --dev
yarn add lint-staged --dev
//package.json
"scripts": {
"test": "eslint ./index.js",
"precommit": "lint-staged"
},
"husky": {
"hooks": {
"pre-commit": "npm run precommit"
}
},
"lint-staged": {
"*.js": [
"eslint",
"git add"
]
}