react開源web項目,React+TypeScript+webpack4多入口項目搭建

 2023-10-15 阅读 19 评论 0

摘要:資源 React-16.8.*react-router-dom-4.3.*TypeScript-3.5.*webpack-4.*eslint-5.16.* 項目目錄 ├── dist # 打包結果目錄 │ ├── demo1 //類別demo1的打包結果 │ │ ├── demo1.himl │ │ ├── demo1.js │ │ └── demo1.css │ └── demo2 ... //類別demo2的

資源

  • React-16.8.*
  • react-router-dom-4.3.*
  • TypeScript-3.5.*
  • webpack-4.*
  • eslint-5.16.*

項目目錄

├── 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 項目來說,管理和使用每個組件的 propsstate 或許會成為一件讓人頭痛的事情,而為每一個組件寫文檔,成本也會比較大,對項目的開發效率也不是最理想的。

TypescriptReact 帶來很多好處:

  • 在組件頭部定義 interface,讓每個人在拿到組件的第一時間就可以很明確知道該組件需要使用的 propsstate
  • 在編譯中發現問題,減少運行時的報錯;
  • 可以在編輯器中實現實時類型校驗、引用查詢;
  • 約束類型,在混合多語言環境中降低風險,等。

需求

要搭建一個React+TypeScript+webpack的項目的話,一般都是團隊開發多人多文件項目,在搭建之前需要優先考慮以下幾個方面:

  • 開發體驗
  • 項目打包
  • 團隊規范

安裝

  • 前置安裝
    首先需要全局安裝typescript,這里默認大家都已經安裝了node以及npm

    npm install -g typescript
    
  • react開源web項目、首先新建文件夾并進入

    mkdir tsDemo && cd tsDemo
    
  • 然后進行初始化,生成package.jsontsconfig.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配置

添加webpack文件

根目錄下新建webpack.config.base.js、webpack.config.build.js、webpack.config.js文件

touch webpack.config.base.js webpack.config.build.js webpack.config.js
  1. entry:入口文件(你要打包,就告訴我打包哪些)
  2. output:出口文件(我打包完了,給你放到哪里)
  3. resolve: 尋找模塊所對應的文件
  4. module:模塊(放lorder,編譯瀏覽器不認識的東西)
  5. plugins:插件(輔助開發,提高開發效率)
  6. externals:打包忽略
  7. devServer:服務器(webpack提供的本地服務器)
  8. mode:模式,分為開發模式、生產模式。此為4.X里新增的

配置entry入口文件

因為大部分項目是多入口,多類別的,所有入口配置時不要配置單一入口

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
};

配置output出口文件

const webpackConfig = {output: {//輸出文件名稱以當前傳入的cate類別名稱命名filename: "[name].js", //輸出到根目錄下的dist目錄中path: path.resolve(__dirname, "dist"),publicPath: "/",},
}

配置resolve

需要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")] },
}

配置module

概念

webpack中任何一個東西都稱為模塊,js就不用說了。一個css文件,一張圖片、一個less文件都是一個模塊,都能用導入模塊的語法(commonjsrequireES6import)導入進來。webpack自身只能讀懂js類型的文件,其它的都不認識。但是webpack卻能編譯打包其它類型的文件,像ES6JSXlesstypeScript等,甚至cssimages也是Ok的,而想要編譯打包這些文件就需要借助loader

react.js框架。loader就像是一個翻譯員,瀏覽器不是不認識這些東西么?那好交給loader來辦,它能把這些東西都翻譯成瀏覽器認識的語言。loader描述了webpack如何處理非js模塊,而這些模塊想要打包loader必不可少,所以它在webpack里顯得異常重要。loader跟插件一樣都是模塊,想要用它需要先安裝它,使用的時候把它放在module.rules參數里,rules翻譯過來的意思就是規則,所以也可以認為loader就是一個用來處理不同文件的規則

所需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文件

module完整配置

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

plugins里面放的是插件,插件的作用在于提高開發效率,能夠解放雙手,讓我們去做更多有意義的事情。一些很low的事就統統交給插件去完成。

const webpackConfig = {plugins: [//清除文件new CleanWebpackPlugin(),//css單獨打包new MiniCssExtractPlugin({filename: "[name].css",chunkFilename: "[name].css"}),// 引入熱更新插件new webpack.HotModuleReplacementPlugin() ]
}

配置externals

前端webpack、如果需要引用一個庫,但是又不想讓webpack打包(減少打包的時間),并且又不影響我們在程序中以CMD、AMD或者window/global全局等方式進行使用(一般都以import方式引用使用),那就可以通過配置externals。

const webpackConfig = {//項目編譯打包是忽略這些依賴包externals: {react: "React","react-dom": "ReactDOM","react-redux": "ReactRedux",}
}

配置mode

modewebpack4新增的一條屬性,它的意思為當前開發的環境。mode的到來減少了很多的配置,它內置了很多的功能。相較以前的版本提升了很多,減少了很多專門的配置

  1. 提升了構建速度
  2. 默認為開發環境,不需要專門配置
  3. 提供壓縮功能,不需要借助插件
  4. 提供SouceMap,不需要專門配置

mode分為兩種環境,一種是開發環境(development),一種是生產環境(production)。開發環境就是我們寫代碼的環境,生產環境就是代碼放到線上的環境。這兩種環境的最直觀區別就是,開發環境的代碼不提供壓縮,生產環境的代碼提供壓縮。

配置devServer

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;

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/140020.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息