2021 年從零開發前端項目指南
之前翻譯過一篇 前端工程化發展歷史 的文章,Webpack
、Babel
、Eslint
現在基本上就是前端項目的標配了。
但工作以後一般很少接觸這些配置,都是在前人配置好的基礎上去寫業務代碼。即使有機會從零配置一個項目,一般也不會自己手動建這些配置文件,直接用 create-react-app
、Ant Design Pro
等自動幫我們生成各個目錄和配置文件就可以了,省時省力。
這篇文章的話就從零手動去配置一個前端項目,會涉及到 Webpack
、React
、Babel
、TypeScript
、Ant Design
、Sass
、Eslint
、Prettier
,本文的話就本着「不求甚解」的態度,主要過一下各個模塊的使用,適合從零一步一步跟着操作。
前端工程化項目是建立在 node.js
環境下的,之後需要安裝各個 npm
包,所以首先電腦必須已經配置好了 node
環境。
新建一個目錄然後執行 npm init
來初始化一個項目。
npm init
然後一路回車就可以,只是生成了 package.json
文件,後續想改的話也能改。
img
Webpack
前端不斷髮展,但很多特性瀏覽器不一定會支持,ES6
模塊,CommonJs
模塊、Scss/less
、jsx
等等,通過 Webpack
我們可以將所有文件進行打包、壓縮混淆,最終轉換爲瀏覽器識別的代碼。
除了安裝 Webpack
,我們需要安裝對應的命令行工具 webpack-cli
,以及實現了熱加載,也就是自動監聽我們文件變化然後刷新網頁的 webpack-dev-server
。
由於這些工具只在開發階段使用,所以我們安裝的時候可以加上 -D(--save-dev)
命令,這樣開發環境就不會打包了。
npm i -D webpack webpack-cli webpack-dev-server
安裝之後 package.json
會自動記錄我們安裝的 node
包,對應版本如下,如果安裝的和我不一樣的話,後邊的一些配置可能略有不同。
{
...
"devDependencies": {
"webpack": "^5.51.1",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.0.0"
}
}
接下來在根目錄新建 webpack.config.js
進行項目的配置,主要配置入口文件,打包輸目錄,以及 devServer
的目錄。
const path = require('path')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js'
},
devServer: {
static: path.resolve(__dirname, './dist')
}
}
新建一下上邊相應的文件。
main.js
文件主要實現在網頁寫 hello world
。
// /src/main.js
document.write('hello world')
新建 dist
目錄,在裏邊新建 index.html
文件,引入 <script src="bundle.js"></script>
。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta >
<title>前端工程化</title>
</head>
<body>
<div id="app" />
<script src="bundle.js"></script>
</body>
</html>
最後在 package.json
新建兩條命令,默認的 test
命令可以直接刪掉了。
...
"scripts": {
"dev": "webpack-dev-server --mode development --open",
"build": "webpack --mode production"
},
...
執行 npm run dev
,此時會自動打開 http://localhost:8080/
。
React
React
可以讓我們專注於構建用戶界面,而不需要再手動維護 dom
元素的更新,當然還可以用 VUE
。
安裝核心庫 react
,以及渲染 Web
的 react-dom
。
npm i react react-dom
修改 src/main.js
體驗一下。
// /src/main.js
import React from 'react';
import ReactDOM from 'react-dom';
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}
}
ReactDOM.render(
React.createElement(Hello, { toWhat: 'World by React' }, null),
document.getElementById('app')
);
npm run dev
看下效果:
這裏會發現上邊都調用了 React.createElement
來創建元素,如果頁面複雜的的話,那一層套一層就太繁瑣了,React
爲我們提供了 JSX
語法來簡化寫法。
讓我們改寫一下:
// /src/main.js
import React from 'react';
import ReactDOM from 'react-dom';
class Hello extends React.Component {
render() {
return <div>Hello {this.props.toWhat}</div>;
}
}
ReactDOM.render(
<Hello toWhat="World by jsx" />,
document.getElementById('app')
);
但此時會發現項目跑不起來了
現在,我們就需要 Babel
了。
Babel
babel
可以爲我們把各種語法、新功能轉換爲瀏覽器所能識別的 js
。這裏我們先安裝一下 babel
以及在 webpack
中使用的 babel-loader
。
npm i -D @babel/core babel-loader
然後在 webpack
中引入 babel-loader
,用來對 js
進行轉換,更改 webpack.config.js
文件。
const path = require('path')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.(js)x?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
],
},
devServer: {
static: path.resolve(__dirname, './dist')
}
}
然後我們來安裝 @babel/preset-react
來轉換 jsx
語法。
npm i -D @babel/preset-react
在根目錄新建 babel
的配置文件 babel.config.json
。
// babel.config.json
{
"presets": [
"@babel/preset-react"
]
}
此時再運行 npm run dev
就發現項目成功跑起來了!
然後我們還可以安裝一些其他 babel
以便使用最新的 ES
語法,比如箭頭函數、async await
、問號表達式等等, 需要什麼就可以配置什麼。當瀏覽器不支持這些特性時,babel
可以幫我們實現 polyfill
進行降級。
@babel/preset-env
包含了許多 ES
的新特性,core-js
實現 ployfill
,通過這兩個 babel
各種 ES
最新的特性就都可以放心使用了,如果有不滿足的我們可以單獨配置 babel
的插件。
npm i -D @babel/preset-env core-js
然後我們再修改下 babel
的配置文件。
// babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
],
"@babel/preset-react"
],
"plugins": [
]
}
其中 useBuiltIns": "usage"
代表自動判斷每個文件是否引入 ployfill
,corejs: 3
是指定版本。
TypeScript
越來越多的項目引入了 TypeScript
,尤其是規模比較大的項目,通過 ts
可以讓一些 bug
提前暴露,平時自己開發的話也可以引入 ts
,提前瞭解學習。
項目引入 ts
的話有兩種方式:
-
使用
TypeScript Compiler (TSC)
將ts
編譯爲ES5
以便能夠在瀏覽器中運行。並且使用TSC
進行類型檢查。 -
使用
Babel
翻譯TS
,使用TSC
進行類型檢查。
這裏的話使用第二種方式,讓 Babel
和 TSC
各司其職。
首先安裝 TypeScript
以及 React
的 type
。
npm i -D typescript @types/react @types/react-dom
根目錄新建 tsconfig.json
進行 ts
的配置。
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": [
"dom"
],
"jsx": "react",
"noEmit": true,
"sourceMap": true,
/* Strict Type-Checking Options */
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
},
"include": [
"src"
]
}
"noEmit": true,
表明 ts
只做類型檢查,不進行編譯輸出。
然後我們將 src/main.js
修改爲 src/main.tsx
,並且加上類型。
// /src/main.js
import * as React from 'react';
import * as ReactDOM from 'react-dom';
type Props = {
toWhat: string;
};
type State = {
};
class Hello extends React.Component<Props, State> {
render() {
return <div>Hello {this.props.toWhat}</div>;
}
}
ReactDOM.render(
<Hello toWhat="World by jsx" />,
document.getElementById('app')
);
接下來進行 babel
的配置,安裝 @babel/preset-typescript
,將我們代碼從 ts
轉爲 js
。
npm i -D @babel/preset-typescript
babel
配置文件中加入。
// babel.config.json
{
"presets": [
"@babel/preset-typescript",
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
],
"@babel/preset-react"
],
"plugins": [
]
}
最後在 webpack.config.js
中 babel
匹配的路徑中加入 tsx
。
const path = require('path')
module.exports = {
entry: './src/main.tsx',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.(js|ts)x?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
],
},
resolve: {
// 引入模塊的時候可以省略這些後綴
extensions: ['.tsx', '.ts', '.jsx', '.js'],
},
devServer: {
static: path.resolve(__dirname, './dist')
}
}
我們可以全局安裝一下 typescript
,便於使用 tsc
命令進行類型檢查。
npm install -g typescript
可以運行一下 tsc -w
實時進行類型檢查。
Ant Design
引入組件庫,方便更快的開發。
npm install antd
順便可以按照習慣把 main.tsx
中的 hello
組件抽離出來並且命名爲 app.tsx
。
// /src/App.tsx
import * as React from 'react';
import { DatePicker } from 'antd';
type Props = {
toWhat: string;
};
type State = {
};
class App extends React.Component<Props, State> {
render(): JSX.Element {
return <div>
Hello {this.props.toWhat}
<div>
<DatePicker></DatePicker>
</div>
</div>;
}
}
export default App;
然後我們在 main.tsx
引入 antd
的 css
文件。
// /src/main.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import App from './App'
ReactDOM.render(
<App toWhat="World by jsx" />,
document.getElementById('app')
);
此時就需要在 webpack.config.js
配置文件中補上 css
的 loader
,先安裝一下。
npm i -D style-loader css-loader
css-loader
可以讓我們在 js
中引入 css
,style-loader
幫我們將 css
以 style
標籤的形式插入到頁面。
安裝好後進行配置 loader
。
const path = require('path')
module.exports = {
entry: './src/main.tsx',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.(js|ts)x?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
resolve: {
// 引入模塊的時候可以省略這些後綴
extensions: ['.tsx', '.ts', '.jsx', '.js'],
},
devServer: {
static: path.resolve(__dirname, './dist')
}
}
然後就成功引入日期選擇器了。
Sass
Sass
是 css
的預編譯器,可以讓我們寫樣式更順手,具體特性可以參考 官網,我用的最多的就是可以嵌套形式寫 css
,很方便。
我們安裝一下 Sass
以及它的 loader
。
npm install sass-loader sass --save-dev
然後在 webpack.config.js
配置一下
const path = require('path');
module.exports = {
entry: './src/main.tsx',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.(js|ts)x?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.s[ac]ss$/i,
use: [
// 將 JS 字符串生成爲 style 節點
'style-loader',
// 將 CSS 轉化成 CommonJS 模塊
'css-loader',
// 將 Sass 編譯成 CSS
'sass-loader',
],
},
],
},
resolve: {
// 引入模塊的時候可以省略這些後綴
extensions: ['.tsx', '.ts', '.jsx', '.js'],
},
devServer: {
static: path.resolve(__dirname, './dist'),
},
};
在 App.jsx
加幾個類名,引入 App.scss
。
// /src/App.tsx
import * as React from 'react';
import { DatePicker } from 'antd';
import './App.scss';
type Props = {
toWhat: string;
};
type State = {};
class App extends React.Component<Props, State> {
render(): JSX.Element {
return (
<div class>
<div class>Hello</div>
<div>{this.props.toWhat}</div>
<div>
<DatePicker></DatePicker>
</div>
</div>
);
}
}
export default App;
新建 App.scss
,添加顏色實驗一下。
.app {
.text {
color: #f00;
}
}
npm run dev
看下效果
Eslint
可以配置 eslint
來進行語法上靜態的檢查,也可以對 ts
進行檢查。
npm i eslint -D
可以全局安裝一下 npm i -g npx
命令,能夠更方便的運行 node_modules/.bin
目錄下的命令.
不然的話我們要執行 eslint
命令的話需要執行 ./node_modules/.bin/eslint --version
才能取到。或者像上邊爲了執行 tsc
命令,全局安裝了 typescript
。或者在 package.json
裏邊添加一個自定義命令。不過還是 npx
是最方便的。
讓我們初始化 eslint
.
npx eslint --init
然後按照項目需要選擇對應的選項,最後自動安裝相應的依賴。
然後 eslint
就自動爲我們生成了 .eslintrc.js
配置文件,順便補一個 "node": true
,不然的話 module.exports
直接報錯。
module.exports = {
"env": {
"browser": true,
"es2021": true,
"node": true,
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react",
"@typescript-eslint"
],
"rules": {
}
};
然後我們在 package.json
中可以添加一個 lint
命令來修復代碼。
{
"name": "fe-learn",
"version": "1.0.0",
"description": "前端工程化項目學習",
"main": "index.js",
"scripts": {
"dev": "webpack-dev-server --mode development --open",
"build": "webpack --mode production",
"lint": "eslint src --fix"
},
"author": "windliang",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@types/react": "^17.0.19",
"@types/react-dom": "^17.0.9",
"@typescript-eslint/eslint-plugin": "^4.29.2",
"@typescript-eslint/parser": "^4.29.2",
"babel-loader": "^8.2.2",
"core-js": "^3.16.2",
"eslint": "^7.32.0",
"eslint-plugin-react": "^7.24.0",
"typescript": "^4.3.5",
"webpack": "^5.51.1",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.0.0"
},
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
}
}
然後執行 npm run lint
即可進行 eslint
的相關修復。
配合 Vscode
我們也可以做到邊寫代碼邊自動檢測 eslint
,以及保存的時候自動修復 eslint
相關錯誤。
可以安裝 Eslint
插件,以及在 vscode
的設置中加入以下配置,點擊下圖的右上角可以直接進行配置的編輯。
{
"eslint.validate": ["javascript", "javascriptreact", "vue", "typescript", "typescriptreact"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
}
爲了使用更完善的 eslint
配置,我們也可以直接引用騰訊 Alloy
團隊的推薦配置,參考 這裏。
Prettier
prettier
主要做代碼風格上的檢查,字符串雙引號還是單引號?幾個空格?類似這樣的。
當然 eslint
也可以配置這些,但爲了分離它們各自的職責,最好還是用 prettier
來格式化代碼風格,先安裝一下。
npm i -D prettier
然後新建一個配置文件 .prettierrc.js
,這裏直接引用 騰訊 Alloy 團隊推薦的配置。
// .prettierrc.js
module.exports = {
// max 120 characters per line
printWidth: 120,
// use 2 spaces for indentation
tabWidth: 2,
// use spaces instead of indentations
useTabs: false,
// semicolon at the end of the line
semi: true,
// use single quotes
singleQuote: true,
// object's key is quoted only when necessary
quoteProps: 'as-needed',
// use double quotes instead of single quotes in jsx
jsxSingleQuote: false,
// no comma at the end
trailingComma: 'all',
// spaces are required at the beginning and end of the braces
bracketSpacing: true,
// end tag of jsx need to wrap
jsxBracketSameLine: false,
// brackets are required for arrow function parameter, even when there is only one parameter
arrowParens: 'always',
// format the entire contents of the file
rangeStart: 0,
rangeEnd: Infinity,
// no need to write the beginning @prettier of the file
requirePragma: false,
// No need to automatically insert @prettier at the beginning of the file
insertPragma: false,
// use default break criteria
proseWrap: 'preserve',
// decide whether to break the html according to the display style
htmlWhitespaceSensitivity: 'css',
// vue files script and style tags indentation
vueIndentScriptAndStyle: false,
// lf for newline
endOfLine: 'lf',
// formats quoted code embedded
embeddedLanguageFormatting: 'auto',
};
同樣的,爲了保存的時候自動幫我們格式化,我們可以安裝 Vscode
的 Prettier
插件,以及再修改 Vscode
的配置。
{
"files.eol": "\n",
"editor.tabSize": 2,
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.validate": ["javascript", "javascriptreact", "vue", "typescript", "typescriptreact"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
總結
通過上邊一系列的操作後,就可以開始愉快的開始寫項目了,經驗有限,上邊有問題的地方還請大家指出。
上邊的代碼都比較零碎,可以在 github 上看整個代碼,後臺回覆「前端配置」即可得到鏈接。
上邊每一塊都是一個很大的地方,未來的話會繼續邊學習邊總結,歡迎一起交流。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/_xAT8D9O-O6Jz1cQ3sUhnA