├── dist # 打包結果目錄
│ ├── demo1 //類別demo1的打包結果
│ │ ├── demo1.himl
│ │ ├── demo1.js
│ │ └── demo1.css
│ └── demo2 ... //類別demo2的打包結果
├── src # 業務資源文件目錄
│ ├── category //項目分類
│ │ ├── demo1
│ │ ├── demo2
│ │ └── ...
│ ├── components //公共組件
│ ├── util //公共資源
│ └── custom.d.ts //項目全局變量聲明文件
├── index.html //項目啟動入口
├── .gitignore //git忽略文件
├── .eslintrc.js //eslint校驗配置
├── package.json //依賴包
├── tsconfig.json //ts配置
├── webpack.config.build.js //webpack打包
├── webpack.config.base.js //webpack基礎配置
└── webpack.config.js //項目啟動配置
對于復雜或多人開發的 React
項目來說,管理和使用每個組件的 props
、 state
或許會成為一件讓人頭痛的事情,而為每一個組件寫文檔,成本也會比較大,對項目的開發效率也不是最理想的。
Typescript
給 React
帶來很多好處:
interface
,讓每個人在拿到組件的第一時間就可以很明確知道該組件需要使用的 props
和 state
;要搭建一個React+TypeScript+webpack的項目的話,一般都是團隊開發多人多文件項目,在搭建之前需要優先考慮以下幾個方面:
前置安裝
首先需要全局安裝typescript,這里默認大家都已經安裝了node以及npm
npm install -g typescript
react開源web項目、首先新建文件夾并進入
mkdir tsDemo && cd tsDemo
然后進行初始化,生成package.json
和tsconfig.json
npm init -y && tsc --init
安裝開發工具
npm install-D webpack webpack-cli webpack-dev-server
安裝react相關
因為需要整合ts,而react原本的包是不包含驗證包的,所以這里也需要安裝相關ts驗證包
npm install -S react react-dom
npm install -D @types/react @types/react-dom
安裝ts-loader
npm install -D ts-loader
react websocket、以上是基本的 后續會貼出項目demo里面包含所有依賴包
根目錄下新建webpack.config.base.js、webpack.config.build.js、webpack.config.js文件
touch webpack.config.base.js webpack.config.build.js webpack.config.js
entry
:入口文件(你要打包,就告訴我打包哪些)output
:出口文件(我打包完了,給你放到哪里)resolve
: 尋找模塊所對應的文件module
:模塊(放lorder,編譯瀏覽器不認識的東西)plugins
:插件(輔助開發,提高開發效率)externals
:打包忽略devServer
:服務器(webpack提供的本地服務器)mode
:模式,分為開發模式、生產模式。此為4.X里新增的因為大部分項目是多入口,多類別的,所有入口配置時不要配置單一入口
const fs = require("fs");
const path = require("path");
const optimist = require("optimist");const cateName = optimist.argv.cate;
let entryObj = {};
const srcPath = `${__dirname}/src`;
//獲取當前項目要啟動或者打包的基礎路徑
const entryPath = `${srcPath}/category/`;
//未指定類別 啟動或者打包所有類別
//如:npm run dev 或者npm run build
if (cateName == true) {fs.readdirSync(entryPath).forEach((cateName, index) => {// cateName/cateName指定輸出路徑為entrynameif (cateName != "index.html" && cateName != ".DS_Store") entryObj[`${cateName}/${cateName}`] = `${entryPath + cateName}/${cateName}.tsx`;});
} else if (cateName.indexOf(",")) {// 一次指定多個類別 類別之間以","分割//如:npm run dev erhsouche,huoche let cateNameArray = cateName.split(",");for (let i = 0; i < cateNameArray.length; i++) {entryObj[`${cateNameArray[i]}/${cateNameArray[i]}`] = `${entryPath + cateNameArray[i]}/${cateNameArray[i]}.tsx`;}
} else {// 打包單個入口文件//如:npm run dev ershoucheentryObj[`${cateName}/${cateName}`] = `${entryPath + cateName}/${cateName}.tsx`;
}
const webpackConfig = {entry: entryObj,
}
module.exports = {webpackConfig,entryObj
};
const webpackConfig = {output: {//輸出文件名稱以當前傳入的cate類別名稱命名filename: "[name].js", //輸出到根目錄下的dist目錄中path: path.resolve(__dirname, "dist"),publicPath: "/",},
}
需要import xxx from 'xxx'
這樣的文件的話需要在webpack中的resolve
項中配置extensions
,這樣以后引入文件就不需要帶擴展名
const webpackConfig = {resolve: {extensions: [".tsx", ".ts", ".js", ".jsx", ".json"],//配置項通過別名來把原導入路徑映射成一個新的導入路徑。alias: {images: path.join(__dirname, "src/util/img")},// 使用絕對路徑指明第三方模塊存放的位置,以減少搜索步驟modules: [path.resolve(__dirname, "node_modules")] },
}
在webpack
中任何一個東西都稱為模塊,js
就不用說了。一個css
文件,一張圖片、一個less
文件都是一個模塊,都能用導入模塊的語法(commonjs
的require
,ES6
的import
)導入進來。webpack
自身只能讀懂js
類型的文件,其它的都不認識。但是webpack
卻能編譯打包其它類型的文件,像ES6
、JSX
、less
、typeScript
等,甚至css
、images
也是Ok的,而想要編譯打包這些文件就需要借助loader
react.js框架。loader
就像是一個翻譯員,瀏覽器不是不認識這些東西么?那好交給loader
來辦,它能把這些東西都翻譯成瀏覽器認識的語言。loader
描述了webpack
如何處理非js
模塊,而這些模塊想要打包loader
必不可少,所以它在webpack
里顯得異常重要。loader
跟插件一樣都是模塊,想要用它需要先安裝它,使用的時候把它放在module.rules
參數里,rules
翻譯過來的意思就是規則,所以也可以認為loader
就是一個用來處理不同文件的規則
ts-loader
編譯TypeScript文件
npm install ts-loader -D
url-loader
處理css中的圖片資源時,我們常用的兩種loader是file-loader或者url-loader,兩者的主要差異在于。url-loader可以設置圖片大小限制,當圖片超過限制時,其表現行為等同于file-loader,而當圖片不超過限制時,則會將圖片以base64的形式打包進css文件,以減少請求次數。
npm install url-loader -D
react typescript,css處理所需loader
css-loader 處理css
sass-loader 編譯處理scss
sass-resources-loader 全局注冊變量
html-loader
處理.html文件
const webpackConfig = {module: {rules: [//處理tsx文件{ test: /\.(tsx|ts)?$/, use: ["ts-loader"], include: path.resolve(__dirname, "src") },//處理圖片資源{test: /\.(png|jpe?g|jpg|gif|woff|eot|ttf|svg)/,use: [// 對非文本文件采用file-loader加載{loader: "url-loader",options: {limit: 1024 * 30, // 30KB以下的文件name: "images/[name].[hash:8].[ext]",}}],},//處理css和scss{test: /\.(css|scss)$/,use: [//css單獨打包MiniCssExtractPlugin.loader,{loader: "css-loader"},{loader: "postcss-loader",options: {plugins: () => [require("autoprefixer")],sourceMap: true}},{loader: "sass-loader",options: {sourceMap: true}},{loader: "sass-resources-loader",options: {resources: ["./skin/mixin.scss", "./skin/base.scss"]}}],exclude: path.resolve(__dirname, "node_modules")},{test: /\.html$/,use: {loader: "html-loader",}},{ test: /src\/containers(\/.*).(tsx|ts)/, loader: "bundle-loader?lazy!ts-loader" },{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" }]},
}
plugins
里面放的是插件,插件的作用在于提高開發效率,能夠解放雙手,讓我們去做更多有意義的事情。一些很low的事就統統交給插件去完成。
const webpackConfig = {plugins: [//清除文件new CleanWebpackPlugin(),//css單獨打包new MiniCssExtractPlugin({filename: "[name].css",chunkFilename: "[name].css"}),// 引入熱更新插件new webpack.HotModuleReplacementPlugin() ]
}
前端webpack、如果需要引用一個庫,但是又不想讓webpack打包(減少打包的時間),并且又不影響我們在程序中以CMD、AMD或者window/global全局等方式進行使用(一般都以import方式引用使用),那就可以通過配置externals。
const webpackConfig = {//項目編譯打包是忽略這些依賴包externals: {react: "React","react-dom": "ReactDOM","react-redux": "ReactRedux",}
}
mode
是webpack4
新增的一條屬性,它的意思為當前開發的環境。mode
的到來減少了很多的配置,它內置了很多的功能。相較以前的版本提升了很多,減少了很多專門的配置
- 提升了構建速度
- 默認為開發環境,不需要專門配置
- 提供壓縮功能,不需要借助插件
- 提供
SouceMap
,不需要專門配置
mode
分為兩種環境,一種是開發環境(development
),一種是生產環境(production
)。開發環境就是我們寫代碼的環境,生產環境就是代碼放到線上的環境。這兩種環境的最直觀區別就是,開發環境的代碼不提供壓縮,生產環境的代碼提供壓縮。
const webpackConfig = {devServer: {// 本地服務器所加載的頁面所在的目錄contentBase: srcPath, //熱更新hot: true,//服務端口port: "7788",// 是否向Chunk中注入代理客戶端,默認注入inline: true, // publicPath: '/dist/',historyApiFallback: {index: "template.html",},//默認檢查hostnamedisableHostCheck: true,compress: true,open: true // 自動打開首頁}
}
webpack.config.base.js完整文件
const fs = require("fs");
const path = require("path");
const webpack = require("webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlguin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // webpack4支持的單獨打包css
// const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); //webpack4支持自動優化代碼 mode:'development'即可
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");const isProduction = process.env.NODE_ENV === "production";const optimist = require("optimist");const cateName = optimist.argv.cate;
let entryObj = {};
const srcPath = `${__dirname}/src`;
const entryPath = `${srcPath}/category/`;
if (cateName == true) {fs.readdirSync(entryPath).forEach((cateName, index) => {// cateName/cateName指定輸出路徑為entrynameif (cateName != "index.html" && cateName != ".DS_Store") entryObj[`${cateName}/${cateName}`] = `${entryPath + cateName}/${cateName}.tsx`;});
} else if (cateName.indexOf(",")) {// 一次打包多個入口文件以逗號分隔let cateNameArray = cateName.split(",");for (let i = 0; i < cateNameArray.length; i++) {entryObj[`${cateNameArray[i]}/${cateNameArray[i]}`] = `${entryPath + cateNameArray[i]}/${cateNameArray[i]}.tsx`;}
} else {// 打包單個入口文件entryObj[`${cateName}/${cateName}`] = `${entryPath + cateName}/${cateName}.tsx`;
}const webpackConfig = {entry: entryObj,output: {filename: "[name].js",path: path.resolve(__dirname, "dist"),publicPath: "/",},resolve: {extensions: [".tsx", ".ts", ".js", ".jsx", ".json"],alias: {images: path.join(__dirname, "src/util/img"),components: path.join(__dirname, "src/components")},modules: [path.resolve(__dirname, "node_modules")] // 使用絕對路徑指明第三方模塊存放的位置,以減少搜索步驟},module: {rules: [{ test: /\.(tsx|ts)?$/, use: ["ts-loader"], include: path.resolve(__dirname, "src") },{test: /\.(png|jpe?g|jpg|gif|woff|eot|ttf|svg)/,use: [// 對非文本文件采用file-loader加載{loader: "url-loader",options: {limit: 1024 * 30, // 30KB以下的文件name: "images/[name].[hash:8].[ext]",}}],},{test: /\.(css|scss)$/,use: [MiniCssExtractPlugin.loader,{loader: "css-loader"},{loader: "postcss-loader",options: {plugins: () => [require("autoprefixer")],sourceMap: true}},{loader: "sass-loader",options: {sourceMap: true}},{loader: "sass-resources-loader",options: {resources: ["./skin/mixin.scss", "./skin/base.scss"]}}],exclude: path.resolve(__dirname, "node_modules")},{test: /\.html$/,use: {loader: "html-loader",}},{ test: /src\/containers(\/.*).(tsx|ts)/, loader: "bundle-loader?lazy!ts-loader" },{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" }]},mode: isProduction ? "development" : "production",// 安全起見,生產環境使用hidden-source-map,會生成詳細的Source Map,但不會將Source Map暴露出去devtool: isProduction ? "hidden-source-map" : "cheap-module-eval-source-map",externals: {react: "React","react-dom": "ReactDOM","react-redux": "ReactRedux",},plugins: [new CleanWebpackPlugin(),new MiniCssExtractPlugin({filename: "[name].css",chunkFilename: "[name].css"}),new webpack.HotModuleReplacementPlugin() // 引入熱更新插件]
};const pages = Object.keys(entryObj);pages.forEach(pathname => {const htmlName = entryObj[pathname];const template_local = htmlName.replace(".tsx", ".html");const entryName = pathname.split("/")[0];let conf = {filename: `category/${entryName}/${entryName}.html`, // 生成的html存放路徑,相對于pathtitle: entryName,template: template_local, // html模板路徑inject: true, // js插入的位置,true/'head'/'body'/falsehash: true, // 為靜態資源生成hash值// favicon: 'src/favicon.ico', //favicon路徑,通過webpack引入同時可以生成hash值chunks: [pathname],minify: {// 壓縮HTML文件removeComments: true, // 移除HTML中的注釋collapseWhitespace: false, // 刪除空白符與換行符},};const defineConf = Object.assign({}, conf, { template: "src/template.html" });const exists = fs.existsSync(template_local);if (exists) {webpackConfig.plugins.push(new HtmlWebpackPlguin(conf));} else {webpackConfig.plugins.push(new HtmlWebpackPlguin(defineConf));}
});module.exports = {webpackConfig,entryObj
};
webpack.config.js完整文件
const webpack = require("webpack");
const { webpackConfig } = require("./webpack.config.base");
let optimist = require("optimist");let cateName = optimist.argv.cate;
const srcPath = `${__dirname}/src`;
webpackConfig.devServer = {contentBase: srcPath, // 本地服務器所加載的頁面所在的目錄hot: true,port: "7788",inline: true, // 是否向Chunk中注入代理客戶端,默認注入// publicPath: '/dist/',historyApiFallback: {index: "template.html",},disableHostCheck: true,compress: true,open: true // 自動打開首頁
};
// 通過插件模式開啟模塊熱替換 也可在執行命令時加上 --hot
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
module.exports = webpackConfig;
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态