• 项目实战【vue,react,微信小程序】(1708E)


    目录

    一、Promise

    二、swiper

    三、$event

    四、transition 过渡动画

    五、toast组件

    六、解构并不能实现对象的深拷贝

    七、修改脚手架默认包管理工具

    八、redux

    九、react组件props类型检查和默认值

    十、使用webpack搭建react开发环境

    github源码:


    一、Promise

    async函数会返回一个promise,并且Promise对象的状态值是resolved(成功的)

        //Promise基础用法
        let pro = new Promise(resolve => {
          setTimeout(() => {
            resolve('hello world')
          }, 500)
        })
    
        pro.then(res => {
          console.log(res) // hello world
        })
    
        //Promise和async await相结合,async函数会返回一个promise,并且Promise对象的状态值是resolved(成功的)
        const fun = async () => {
          let result
          await new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve({
                code: 200,
                data: { name: 'zhangsan' },
                message: '姓名'
              })
            }, 1000)
          }).then(res => {
            result = res
          })
    
          return result
        }
    
        Promise和async await相结合二
        fun().then(res => {
          console.log(res)
        })
    
        const fun2 = async () => {
          return await new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve({
                code: 200,
                data: 'lishi',
                message: '消息'
              })
            }, 1500)
          })
        }
    
        fun2().then(res => {
          console.log(res)
        })
    

    二、swiper

    swiper.js:

    <template>
      <div>
        <div>
          <swiper v-if="bannerList.length > 0" :options="options">
            <swiper-slide v-for="(item,index) in bannerList" :key="index">
              <img :src="item" class="m-banner-img"/>
            </swiper-slide>
            <div class="swiper-pagination" slot="pagination"></div>
            <div class="swiper-button-prev" slot="button-prev"></div>
            <div class="swiper-button-next" slot="button-next"></div>
          </swiper>
        </div>
      </div>
    </template>
    
    <script>
    import Api from "../api";
    
    export default {
      data() {
        return {
          bannerList: [],
          options: {
            loop: true,
            effect: 'cube', //slide, fade,cube, coverflow, flip
            speed: 1000,
            autoplay: {
              delay: 2000
            },
            pagination: {
              el: '.swiper-pagination',
              clickable: true
            },
            navigation: {
              nextEl: '.swiper-button-next',
              prevEl: '.swiper-button-prev'
            }
          }
        };
      },
      mounted() {
        Api.getBanner().then(res => {
          if (res.code === 200) {
            this.bannerList = res.data;
          }
        });
      }
    };
    </script>
    
    <style>
    </style>

    main.js:

    import Vue from 'vue'
    import lazyload from 'vue-lazyload'
    import App from './App.vue'
    import router from './router'
    import store from './store'
    import VueAwesomeSwiper from 'vue-awesome-swiper'
    import 'swiper/dist/css/swiper.css'
    import './font/iconfont.css'
    import './index.css'
    import img from './images/loading.png'
    
    
    Vue.config.productionTip = false
    
    Vue.use(lazyload, {
      loading: img
    })
    
    Vue.use(VueAwesomeSwiper)
    
    new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount('#app')
    

    三、$event

    有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法。

    https://cn.vuejs.org/v2/guide/events.html#%E5%86%85%E8%81%94%E5%A4%84%E7%90%86%E5%99%A8%E4%B8%AD%E7%9A%84%E6%96%B9%E6%B3%95

    四、transition 过渡动画

    <template>
      <div>
        <div>
          <router-link to="/index/home" class="m-nav-item">首页</router-link>
          <router-link to="/index/my_book" class="m-nav-item">书包</router-link>
          <router-link to="/index/exam1" class="m-nav-item">周考1</router-link>
        </div>
        <transition name="slide">
          <router-view class="m-router"></router-view>
        </transition>
      </div>
    </template>
    
    <script>
    export default {
    }
    </script>
    
    <style>
    .m-router{position: absolute;100%}
    .slide-enter-active{transition: all 1s linear;}
    .slide-enter{transform: translateX(100%)}
    
    /* .slide-leave-active{transition: all 1s linear;}
    .slide-leave-to{transform: translateX(-100%)} */
    
    .slide-leave-active{animation: slide 1s linear;}
    
    @keyframes slide {
      0% {transform: translateX(0)}
      100% { transform: translateX(-100%)}
    }
    </style>

    五、toast组件

    Toast.vue:

    <template>
      <div class="m-toast-mask" :id="id" >
        <div class="m-toast">{{message}}</div>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          duration: 3000,
          message: '',
          id: Date.now()
        }
      },
      mounted() {
        setTimeout(() => {
          document.getElementById(this.id).remove()
        }, this.duration)
      }
    }
    </script>
    
    <style>
    
    </style>

    index.js:

    import Vue from 'vue'
    import Toast from './Toast'
    
    const ToastConstructor = Vue.extend(Toast)
    
    const toast = (options) => {
      let instance = new ToastConstructor({data: options}).$mount()
      document.body.appendChild(instance.$el)
    }
    
    export default toast

    css:

    .m-toast-mask{display: flex; position: fixed;top: 0;left: 0;right: 0;bottom: 0;}
    .m-toast{margin: auto;padding: 0 10px; min- 100px;line-height: 40px;border-radius: 3px;background: rgba(0, 0, 0, 0.5);text-align: center;color: #ffffff;}
    

    使用:

    <template>
      <div>
        <div v-for="(item, index) in myBook" :key="item.id" class="m-my-book-item">
          <div class="m-my-book-info">
            <label>
              <input type="checkbox" :checked="item.checked" @change="handleCheck(index, $event)" />
              {{item.title}}
            </label>
            ¥{{item.price}}
          </div>
          <div class="m-my-book-action">
            <button @click="handleSub(index)">-</button>
            {{item.count}}
            <button @click="handleAdd(index)">+</button>
            <button @click="handleDelete(index)">删除</button>
          </div>
        </div>
        <div v-if="myBook.length > 0">
          <div>
            <label>
              <input type="checkbox" :checked="total.checkedAll" @click="handleCheckAll">
              全选
            </label>
            <button @click="handleShowDialog">删除</button>
          </div>
          <div>总价:¥{{total.totalPrice}},总数:{{total.totalCount}}</div>
        </div>
        <div v-else>书包空空如也~~~</div>
        <Dialog :visible="visible" title="删除">
          <template v-slot:content>
            <div class="m-delete-info">
              <Icon type="shanchu" classname="m-delete-icon"></Icon>
              <div class="m-delete-text">
                你确定要删除选中的商品吗?
              </div>
            </div>
          </template>
          <template v-slot:footer>
            <button class="m-btn" @click="handleHideDialog">取消</button>
            <button class="m-btn" @click="handleDeleteChecked">确定</button>
          </template>
        </Dialog>
      </div>
    </template>
    
    <script>
    import Api from "../api";
    import Dialog from '../components/Dialog'
    import Icon from '../components/Icon'
    import toast from '../components/Toast'
    
    export default {
      data() {
        return {
          visible: false
        }
      },
      computed: {
        myBook() {
          return this.$store.state.myBook;
        },
        total() {
          let myBook = this.myBook;
          let totalCount = myBook.filter(item => item.checked).reduce((total, item) => {
            return total + item.count;
          }, 0);
          let totalPrice = myBook.filter(item => item.checked).reduce((total, item) => {
            return total + item.count * item.price;
          }, 0);
          return {
            totalCount,
            totalPrice,
            checkedAll: myBook.every(item => item.checked)
          };
        }
      },
      components:{
        Dialog,
        Icon
      },
      methods: {
        handleSub(index) {
          let myBook = this.myBook;
          if (myBook[index].count > 1) {
            myBook[index].count--;
            this.$store.commit({ type: "setState", key: "myBook", value: myBook });
          }
        },
        handleAdd(index) {
          let myBook = this.myBook;
          myBook[index].count++;
          this.$store.commit({ type: "setState", key: "myBook", value: myBook });
        },
        handleDelete(index) {
          let myBook = this.myBook;
          myBook.splice(index, 1)
          this.$store.commit({ type: "setState", key: "myBook", value: myBook });
        },
        handleCheck(index, e) {
          let myBook = this.myBook;
          myBook[index].checked = e.target.checked
          this.$store.commit({ type: "setState", key: "myBook", value: myBook });
        },
        handleCheckAll(e) {
          let myBook = this.myBook;
          myBook.forEach(item => {
            item.checked = e.target.checked
          })
          this.$store.commit({ type: "setState", key: "myBook", value: myBook });
        },
        handleDeleteChecked() {
          let myBook = this.myBook;
          myBook = myBook.filter(item => !item.checked)
          this.$store.commit({ type: "setState", key: "myBook", value: myBook });
          this.handleHideDialog()
        },
        handleShowDialog() {
          if (this.myBook.filter(item => item.checked).length === 0) {
            //alert('请选择要删除的商品~')
            toast({message: '请选择要删除的商品~', duration: 1000})
            return
          }
          this.visible = true
        },
        handleHideDialog() {
          this.visible = false
        }
      },
      updated() {
        Api.update({ myBookNew: this.myBook }).then(res => {});
      },
      mounted() {
        this.$store.dispatch({ type: "getMyBook" });
      }
    };
    </script>
    
    <style>
    </style>

    注册到全局:

    import toast from './components/Toast'
    
    Vue.prototype.$toast = toast

    使用:

    this.$toast({message: 'test'})

    appendChild() 方法在指定元素节点的最后一个子节点之后添加节点。

    https://www.w3school.com.cn/xmldom/met_element_appendchild.asp

    六、解构并不能实现对象的深拷贝

          let obj = {
            a: 1,
            b: {
              c: '1'
            },
            fun() {
              console.log(1)
            },
            time: new Date(),
            d: undefined
          }
          let newObj = { ...obj }
    
          newObj.b.c = '2'
          console.log(obj)
          console.log(newObj)
          console.log(obj.b.c) // 2

    七、修改脚手架默认包管理工具

    八、redux

    reducer 一定要保持纯净,只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。

    时刻谨记永远不要在克隆 state 前修改它。

    redux-devtools中,我们可以查看到redux下所有通过reducer更新state的记录,每一个记录都对应着内存中某一个具体的state,让用户可以追溯到每一次历史操作产生与执行时,当时的具体状态,这也是使用redux管理状态的重要优势之一.

    若不创建副本,redux的所有操作都将指向内存中的同一个state,我们将无从获取每一次操作前后,state的具体状态与改变,若没有副本,redux-devtools列表里所有的state都将被最后一次操作的结果所取代.我们将无法追溯state变更的历史记录.

    创建副本也是为了保证向下传入的this.props与nextProps能得到正确的值,以便我们能够利用前后props的改变情况以决定如何render组件。

    直接修改state(错误的):

          const defaultState = {
            count: 0
          }
    
          //直接修改改state,所有的state都将被最后一次操作的结果所取代.我们将无法追溯state变更的历史记录
          const reducer = (state = defaultState ) => {
            state.count++
            return state
          }
    
          let previousState = { count: 0 }
          let newState = reducer(previousState)
          console.log(previousState)  //{ count: 1 }
          console.log(newState)       //{ count: 1 }

    深拷贝(性能很差):

          const defaultState = {
            count: 0
          }
    
          //深拷贝可以解决这个问题
          const reducer = (state = defaultState ) => {
            state = JSON.parse(JSON.stringify(state))
            state.count++
            return state
          }
    
          let previousState = { count: 0 }
          let newState = reducer(previousState)
          console.log(previousState)  //{ count: 0 }
          console.log(newState)       //{ count: 1 }

    解构:

          const defaultState = {
            count: 0
          }
    
          //解构可以解决这个问题
          const reducer = (state = defaultState ) => {
            let newState = {...state}
            newState.count++
            return newState
          }
    
          let previousState = { count: 0 }
          let newState = reducer(previousState)
          console.log(previousState)  //{ count: 0 }
          console.log(newState)       //{ count: 1 }

    解构属于浅拷贝,解决不了嵌套的场景:

          const defaultState = {
            obj: {
              count: 0
            }
          }
    
          //解构属于浅拷贝,解决不了嵌套问题
          const reducer = (state = defaultState ) => {
            let newState = {...state}
            newState.obj.count++
            return newState
          }
    
          let previousState = {
            obj: {
              count: 0
            }
          }
          let newState = reducer(previousState)
          console.log(previousState)  //{ obj: { count: 1 } }
          console.log(newState)       //{ obj: { count: 1 } }

    非要用解构,也行:

          const defaultState = {
            obj: {
              count: 0
            }
          }
    
          //解构属于浅拷贝,解决不了嵌套问题,不过可以这样解决,只是比较难理解
          const reducer = (state = defaultState ) => {
            let newObj = {...state.obj}
            newObj.count++
            return {...state, obj: newObj}
          }
    
          let previousState = {
            obj: {
              count: 0
            }
          }
          let newState = reducer(previousState)
          console.log(previousState)  //{ obj: { count: 0 } }
          console.log(newState)       //{ obj: { count: 1 } }

    immutable.js:

    <!DOCTYPE html>
    <html>
    
    <head>
    </head>
    
    <body>
      <div>
        <script src="https://cdn.bootcss.com/immutable/4.0.0-rc.12/immutable.js"></script>
        <script>
    
          const defaultState = Immutable.fromJS({
            obj: {
              count: 0
            }
          })
    
          //推荐使用immutable.js,由Facebook工程师耗时三年完成!
          const reducer = (state = defaultState ) => {
            return state.setIn(['obj', 'count'], Immutable.fromJS(state.getIn(['obj', 'count']) + 1))
          }
    
          let previousState = Immutable.fromJS({
            obj: {
              count: 0
            }
          })
          let newState = reducer(previousState)
          console.log(previousState.getIn(['obj']).toJS())  //{ count: 0 }
          console.log(newState.getIn(['obj']).toJS())       //{ count: 1 }
    
        </script>
      </div>
    
    
    </body>
    
    </html>

    把业务代码提出来:

    <!DOCTYPE html>
    <html>
    
    <head>
    </head>
    
    <body>
      <div>
        <script src="https://cdn.bootcss.com/immutable/4.0.0-rc.12/immutable.js"></script>
        <script>
    
          const defaultState = Immutable.fromJS({
            obj: {
              count: 0
            }
          })
    
          //推荐使用immutable.js,有Facebook工程师耗时三年完成!可以把业务代码提出来,reducer变成动态的key和动态的value
          const reducer = (state = defaultState, action ) => {
            return state.setIn(action.key, Immutable.fromJS(action.value))
          }
    
          let previousState = Immutable.fromJS({
            obj: {
              count: 0
            }
          })
          let action = { key: ['obj'], value: { count : 1} } 
          let newState = reducer(previousState, action)
          console.log(previousState.getIn(['obj']).toJS())  //{ count: 0 }
          console.log(newState.getIn(['obj']).toJS())       //{ count: 1 }
    
        </script>
      </div>
    
    
    </body>
    
    </html>

    九、react组件props类型检查和默认值

    不需要安装prop-types包,脚手架已经默认安装

    import React, { Component } from 'react'
    import PropTypes from 'prop-types'
    
    export default class Icon extends Component {
      // static defaultProps = {
      //   type: 'xingxing'
      // }
      render() {
        let { type, className } = this.props
        return (
          <span className={`icon iconfont icon-${type} ${className ? className : ''}`} onClick={this.props.onClick}></span>
        )
      }
    }
    
    Icon.propTypes = {
      type: PropTypes.string,
      className: PropTypes.string
    }
    
    Icon.defaultProps = {
      type: 'shubao'
    }
    
    

    参考链接:

    https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html#___gatsby

    https://www.npmjs.com/package/prop-types

    十、使用webpack搭建react开发环境

    webpack.config.js:

    const MiniCssExtractPlugin = require('mini-css-extract-plugin')
    const HtmeWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
      mode: 'development',
      devtool: 'source-map',
      entry: __dirname + '/index.js',
      output: {
        path: __dirname + '/dist',
        filename: 'bundle-[hash].js'
      },
      devServer: {
        inline: true,
        hot: true,
        port: 9000,
        contentBase: __dirname + '/dist',
        open: true
      },
      module: {
        rules: [
          {
            test: /\.(js|jsx)$/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ["@babel/preset-react", "@babel/preset-env"]
              }
            },
            exclude: /node_modules/
          }, {
            test: /\.css$/,
            use: [{
              loader: MiniCssExtractPlugin.loader
            }, {
              loader: 'css-loader'
            }]
          }]
      },
      plugins: [
        new HtmeWebpackPlugin({
          template: __dirname + '/public/index.html'
        }),
        new MiniCssExtractPlugin({
          filename: '[name]-[hash].css'
        })
      ]
    }

    package.json:

    {
      "name": "toast",
      "version": "1.0.0",
      "description": "",
      "main": "/toast/index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack",
        "start": "webpack-dev-server"
      },
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "@babel/core": "^7.7.7",
        "@babel/preset-env": "^7.7.7",
        "@babel/preset-react": "^7.7.4",
        "babel-loader": "^8.0.6",
        "css-loader": "^3.4.2",
        "html-webpack-plugin": "^3.2.0",
        "mini-css-extract-plugin": "^0.9.0",
        "react": "^16.12.0",
        "react-dom": "^16.12.0",
        "style-loader": "^1.1.2",
        "webpack": "^4.41.5",
        "webpack-cli": "^3.3.10",
        "webpack-dev-server": "^3.10.1"
      }
    }
    

    github:

    https://github.com/baweireact/m-apps/tree/master/m-app-1708E/react/01%E4%BD%BF%E7%94%A8webpack%E6%90%AD%E5%BB%BAreact%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83

    十一、webpack搭建react项目时在二级路由页面刷新报404的解决办法

    react-router 使用webpack-dev-server做服务器时 单级刷新报错 Cannot GET /xx。多级刷新报错404

      output: {
        path: __dirname + '/dist',
        filename: 'bundle-[hash].js',
        publicPath: '/'   //第二处要添加的
      },
      devServer: {
        inline: true,
        hot: true,
        port: 9000,
        contentBase: __dirname + '/dist',
        open: true,
        historyApiFallback: true  //第一处要添加的
      },

    参考链接:

    https://blog.csdn.net/limonsea/article/details/96865906

    十二、webpack图解

    webpack+webpack-dev-server:

    https://segmentfault.com/q/1010000005767033/a-1020000005767079

    通过node启动webpack-dev-server:

    https://webpack.docschina.org/guides/hot-module-replacement/#%E9%80%9A%E8%BF%87-node-js-api

    devServer.overlay:

    这个配置属性用来在编译出错的时候,在浏览器页面上显示错误,默认是false,可设置为true

    devServer: {
        overlay: true
    }

    十三、import对象解构失败问题

    用惯了es6语法中的解构赋值,在对于import引入的对象进行解构赋值时发现对象变成了undefined

    解决办法:

    // main.js
    import { foo, bar } from "./static"
    
    // static.js
    let foo =  "foo"
    let bar = "bar"
    export { foo, bar }

    参考链接:

    https://blog.csdn.net/momDIY/article/details/88375677

    十四、FileZilla连接ftp服务器失败,提示"AUTH TLS"解决方法

    https://www.cnblogs.com/mytt/p/6684199.html

    github源码:

    https://github.com/baweireact/m-apps/tree/master/m-app-1708E

  • 相关阅读:
    Java进阶 -- 文章汇总
    Java并发编程 -- 文章汇总
    大话设计模式读书笔记--文章汇总
    Java并发编程--6.Exchanger线程间交换数据
    Java并发编程--7.Java内存操作总结
    Java并发编程--5.信号量和障碍器
    Java并发编程--4.Executor框架
    Java并发编程--3.Lock
    Java并发编程--2.synchronized
    Java并发编程--1.Thread和Runnable
  • 原文地址:https://www.cnblogs.com/xutongbao/p/14876302.html
Copyright © 2020-2023  润新知