# up **Repository Path**: guokaiGit/up ## Basic Information - **Project Name**: up - **Description**: node + webpack + mongodb dome - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2020-05-21 - **Last Updated**: 2023-07-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # up ## Build Setup #### node npm install && node index #### webpack npm install && npm start #### mongodb mongodb/bin/mongo.exe # 项目踩坑以及心得记录 ## webpack部分 #### node 升级出现 sass不兼容问题 解决方法 npm rebuild node-sass && npm update && npm start #### webpack 各种loader配置 详细配置见webpack.config.js 1. 兼容.vue文件 * 依赖 vue-loader vue-loader/lib/plugin webpack4 需要v15以上且需要(vue-loader/lib/plugin)辅助 * 参考 https://www.jianshu.com/p/1fc5b5151abf 2. css * 依赖 css-loader,less,less-loader,node-sas,sass-loader,style-loader 3. css 提取和压缩 * 依赖 optimize-css-assets-webpack-plugin(提取) mini-css-extract-plugin(压缩) * css 参考 https://www.jianshu.com/p/a1a4ac781a41 (提取和压缩百度以上两种新依赖的使用方式) * 提取和压缩css不支持热更新,开发环境选择使用style-loader 4. js * 依赖 @babel/cli,@babel/core,@babel/plugin-proposal-class-properties,@babel/plugin-transform-runtime,@babel/preset-env,babel-loader * 需要创建 .babelrc 拟定转换规则 * webpack4生产环境默认压缩js * js 参考 https://blog.csdn.net/qq_32935485/article/details/90721095 5. 图片base64转码 依赖 url-loader * 图片参考 https://www.jianshu.com/p/8fea32827ede * url-loader 设置 esModule 为 false 6. base64理解 * 将图片转化为base64以后图片大小会变大,但是会整合到js或者css中,可以有效的减少网络请求的数量,对于较小的图片这个做法可以有效的减少网络消耗。但是较大图片则不建议,会使图片体积增大不利于页面的加载。 7. html的压缩 * 依赖 html-webpack-plugin * html参考 https://blog.csdn.net/qq_34035425/article/details/83042701 * 模板html需要配置变量才可以使title可配置 8. es6+高级语法装换 * 依赖 @babel/preset-env,@babel/core,babel-loader(8.x),@babel/runtime,@babel/plugin-transform-runtime * webpack.config.js 配置loader * 语法转化参考 https://blog.csdn.net/qq_33725202/article/details/89289020 * .babelrc 配置语法装换规则 9. less全局变量的配置 * 依赖 style-resources-loader * webpack.config.js 配置less的loader * 参考文献 https://www.jianshu.com/p/18271f6c99bd 10. 编译地址添加本地ip * webpack.config.js 中配置 devServer.host 为 "0.0.0.0" 11. 支持es7 async/await * 依赖 @babel/plugin-transform-async-to-generator,@babel/plugin-transform-runtime * webpack.config.js 配置loader的plugins 或者 .babelrc的plugins #### webpack代理 * webpack.config.js 中配置 devServer.proxy ````javascript proxy: { //代理 '/api': { target: 'http://192.167.1.175:3000/', //代理地址 changeOrigin: true, //非https使用https不检测 secure: false, pathRewrite: { '^/api': '' } //替换 } } ```` #### webpack 环境配置 * webpack.config.js中的mode 变量可配置当前环境 * 可设置 process.env.NODE_ENV 为环境全局变量方便与开发 * 需要依赖 cross-env * package.json中可以设置环境的全局变量 ````javascript "watch": "cross-env NODE_ENV=development webpack-dev-server --hot", "build": "cross-env NODE_ENV=production webpack", ```` * 例如在baseUrl中的使用 ````javascript if (process.env.NODE_ENV === "development") { //开发环境 baseUrl.public = "/api/"; //代理 } else if (process.env.NODE_ENV === "production") { //生产环境 baseUrl.public = "http://192.167.1.175:3000/"; }; ```` #### webpack 编译和打包文件地址配置 ````javascript //js部分 output: { path: path.resolve(__dirname, './dist'), // 项目的打包文件路径 publicPath: process.env.NODE_ENV === "production" ? './' : '/', // 通过devServer访问路径(区分环境) filename: process.env.NODE_ENV === "production" ? 'js/[name][hash].js' : 'main.js', // 打包后的文件名(区分环境) }, //css和html部分 plugins: [ new MiniCssExtractPlugin({ filename: process.env.NODE_ENV === "production" ? "css/[name][hash].css" : "main.css" //根据环境导出css }), new OptimizeCSSAssetsPlugin(), new VueLoaderPlugin(), new HtmlWebpackPlugin({ title: "webpack", template: './src/index.html', //指定要打包的html路径和文件名 filename: './index.html' //指定输出路径和文件名 }), ] //html可以设置俩份 src中的为打包时的模板 根目录下的为开发时使用 //或者将服务器打开默认地址添加到/src ```` #### webpack 编译动态配置启动服务器时当前的ip地址 ````javascript //ip.js const os = require('os'); module.exports = function () { let needHost = ''; // 打开的host try { // 获得网络接口列表 let network = os.networkInterfaces(); for (let dev in network) { let iFace = network[dev]; for (let i = 0; i < iFace.length; i++) { let alias = iFace[i]; if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal && alias.address.includes("192")) { needHost = alias.address; } } } } catch (e) { needHost = 'localhost'; } return needHost || 'localhost' } //webpack.config.js const ip = require("./build/ip"); devServer.host = ip() //host为启动ip地址设置即可 ```` #### webpack 文件路径别名 ````javascript resolve:{ //设置别名 alias: { '@': path.resolve('src')//@指向src目录 } }, ```` #### 双入口文件打包配置 1. webpack.config.js 中配置 * 可以在各自的文件夹中配置不同的html模板 ````javascript entry:{ // 项目的入口文件,webpack会从main.js开始,把所有依赖的js都加载打包 vue: './src/main.js', react: './react/main.js', vendor: [ //长期静态文件提取 "axios", "fastclick" ], vendorVue: [ 'vue', "vue-router", "wc-messagebox", "vuex", ] }, output: { path: path.resolve(__dirname, './dist'), // 项目的打包文件路径 publicPath: process.env.NODE_ENV === "production" ? './' : '/', // 通过devServer访问路径(区分环境)项目静态文件打包地址,可以写绝对路径如cdn地址 //hash 每次生成的都不一样 chunkhash 只有改变的文件hash才会改变 数字为生成hash的位数 filename: process.env.NODE_ENV === "production" ? 'js/[name][chunkhash:8].js' : 'js/[name].js', // 打包后的文件名(区分环境) chunkFilename: process.env.NODE_ENV === "production" ? 'js/[name][chunkhash:8].js' : 'js/[name].js', // 打包后的文件名(区分环境)(需要异步加载的文件打包到这个路径) }, plugins:[ new HtmlWebpackPlugin({ //可以new多个就形成了每个入口指定的模板和输出 title: "vue", //生成页面的标题 template: './src/index.html', //指定要打包的html路径和文件名 filename: './index.html', //指定输出路径和文件名 chunks: ['vue', 'vendor', 'vendorVue'], }), new HtmlWebpackPlugin({ //可以new多个就形成了每个入口指定的模板和输出 title: "react", //生成页面的标题 template: './react/index.html', //指定要打包的html路径和文件名 filename: './react.html', //指定输出路径和文件名 chunks: ['react', 'vendor'], //指定打包的js 名字为 entry中配置的键值 }), ] ```` 2. 多入口不同编译配置策略 * 某系部分需要单独配置loader编译方式,且需要不影响其他部分 * module.rules 中配置采用 include 和 exclude * include : 将给定路径部分进行编译 * exclude : 除去给定路径部分以外的部分进行编译 * 需要单独配置的部分 使用 include 选中该部分路径 * 其他的部分 使用 exclude 选中单独配置部分的路径 ## vue部分 #### js操作img路径 v-bind:src * 编译后会出现图片路径不正确的问题 * 需要使用 import 或者 require 将图片路径引入为变量解决 #### 父子组件传递对象,实现单向绑定 * 父子组件传递对象实际只传递了堆内存的地址,并未深拷贝对象,需要手动拷贝 * 操作如下 * 父组件正常入参 * 子组件做对象隔绝 ````javaScript props:{ //接收对象 message: { type: Object, default() { return {} } } } computed: { //修改this.message会改变data但是修改data this.message不会改变 data() { return Object.assign({}, this.message); } }, ```` #### 动态引入组件 * component 可以绑定:is,切换is的绑定值实现动态组件 * is可以绑定 --- 已注册组件的名字 或者 一个组件的选项对象 ````javaScript //html // //js this.header = ()=>import('@vue/components/header.vue') //或者 this.header = (a)=>require([''@vue/sacomponents/header.vue'],a) ```` #### 解析()=>import('@vue/components/header.vue') 和 (a)=>require(['@vue/components/header.vue'],a) 1. ()=>import('@vue/components/header.vue') 和 (a)=>require(['@vue/components/header.vue'],a) 在功能上是等价的 2. import 使用的为 Promise 而 (a)=>require(['@vue/components/header.vue'],a) 仍在使用回调函数的方式入参a为回调函数 ## react部分 #### 路由 1. react-router BrowserRouter和HashRouter的区别 * BrowserRouter 服务器地址 类似于vue // mode: 'history', // 去掉url中的# * HashRouter 浏览器地址,会在地址栏出现 #/ * 服务器地址会去请求跳转的路径,如果服务器未配置会返回404 * 浏览器地址带#号 #号后面的地址变化不会请求服务器以实现本地路由切换 * BrowserRouter 包裹最外层即可 * HashRouter 需要包裹每个块级路由 ````jsx
{/* HashRouter 添加路由哈希必须包裹所以路由 */} ( )} />
```` 2. 路由懒加载 (依赖 react-loadable) ````javaScript // 封装方法 @react/utils/loadable.js import React from 'react'; import Loadable from 'react-loadable'; const loadingComponent =()=>{//通用的过场组件 return (
loading...
) } export default (loader,loading = loadingComponent)=>{ //过场组件默认采用通用的,若传入了loading,则采用传入的过场组件 return Loadable({ loader, loading }); } //调用 import loadable from '@react/utils/loadable'; //路由懒加载 const box = loadable(()=>import('@react/views/box/index.jsx')); //路由懒加载写法 //正常使用即可 ```` 3. 路由传参 ````javaScript // //路由配置 this.props.history.push({ // pathname: '/box', pathname: '/box/' + this.state.nowTime, //需要配置路由(地址栏) search: '?time=' + this.state.nowTime, //需要自己解析(地址栏) query: { //(非地址栏,刷新会消失) time: this.state.nowTime }, state: { //(非地址栏,刷新会消失) time: this.state.nowTime } }); ```` * 接受参数可以在props中拿到 #### redux配置 * 可以利用 combineReducers 灵活的区分模块组装理想的树状结构 * action 和 Reducer 函数要尽量设置为纯函数 * action 函数返回一个对象,对象必须有的一个属性为type,其次可以添加自定义属性 * Reducer 函数需要设置初始化变量 以及需要自己手动判断 action.type ````javaScript //构建action函数 export function commonNum(commonNum) { return { type: COMMON_NUM, commonNum } } const stateData = { //初始值 commonNum: Number(localStorage.getItem("commonNum")) || 0 } //Reducer函数 function commonNumReducer(state = stateData.commonNum, action) { switch (action.type) { case COMMON_NUM: const commonNum = action.commonNum || 0 localStorage.setItem("commonNum", commonNum); return Number(commonNum) default: return state; } } const reducer = combineReducers({ common }) const store = createStore(reducer) //监听器,每次调用dispatch执行 返回注销监听器的方法 const unsubscribe = store.subscribe(() => console.log(store.getState())) store.dispatch(commonNum(10)) store.dispatch(commonMessage({ num: 10, type: "number", name: "age" })) //注销监听器 unsubscribe() //注入配置 import { Provider } from 'react-redux'; //通过 Provider 注入 store import store from "@react/redux/index"; ReactDOM.render( ( ) , document.getElementById('root')); //使用connect //引入 connect 和 action函数commonNum import { connect } from 'react-redux'; import { commonNum } from "@react/redux/common/action.js"; function mapDispatchToProps(dispatch) { //声明mapDispatchToProps return { add: (num) => { num++; dispatch(commonNum(num)) }, min: (num) => { num--; dispatch(commonNum(num)) }, } } export default connect((state, props) => { return { num: state.common.commonNumReducer } }, mapDispatchToProps)(RankingList); //方法和变量都会绑定到props上即可使用 ```` #### react 挂载全局api ````javaScript React.Component.prototype.$utils = utils //公共api挂载全局变量 (不建议) ```` #### class申明私有变量编译报错 * 安装@babel/plugin-proposal-class-properties * .babelrc 配置 plugins:["@babel/plugin-proposal-class-properties"] #### 解决react css 类名命名冲突 1. 模块化Css ````javaScript //引入 import style from './index.css' //使用
```` 2. webpack 配置 css loader * 安装style-resources-loader ````javaScript { loader: "css-loader", options: { modules: { // localIdentName 已经迁移到 modules 内不再与modules平级 localIdentName: '[path][name]__[local]--[hash:base64:5]' } } } ```` 3. less全局变量配置 * webpack 配置 less loader ````javaScript { { loader: "css-loader", options: { //打包成css时设置模块化css modules: { localIdentName: '[path][name]__[local]--[hash:base64:5]' } } }, { loader: "less-loader", }, { loader: 'style-resources-loader', options: { //设置less变量 patterns: [path.resolve(__dirname, './src/style/common.less'),path.resolve(__dirname, './react/style/common.less')], injector: 'append' } }, } ```` #### 动态组件引入示例 使用require引入即可,可写成变动函数将值 return出来,然后再render函数中调用 ````jsx render() { const Headers = require("@react/components/header/index.jsx").default return (
) }; ```` ## node部分 #### post 请求解析中间件 ````javascript const express = require('express'); const app = express(); app.use(express.json()); app.use(express.urlencoded({ extended: true })); ```` #### 处理跨域以及token传输 ````javascript app.all('*', function (req, res, next) { res.header("Access-Control-Allow-Origin", "*");//项目上线后改成页面的地址 //允许前端在header传送的字段添加 例如:token Content-Type res.header("Access-Control-Allow-Headers","X-Requested-With,Content-Type,token"); //允许前端使用的请求方式 res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS"); next(); }); ````