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