项目小知识

239 阅读3分钟

Vue项目总结

做这个项目前期比较生疏,后来就挺上手的了,这里总结一些项目中用到的小知识

路由跳转
嵌套

子页面在children里配置,如:path:'/home'带 / 路径是在根目录下,path:'home'不带 / 路径跟在其主页面路径后。redirect路由重定向,使用一个新的路由替换访问的目标路由

{
    path: '/home',
    name: 'Home',
    component: Home,
    redirect: '/welcome',
    children: [
      {
        path: '/users',
        component: User
      },
    ]
  }
传参

参数传递可以使用query和params,区别:query显示在路径上;params不会显示在路径上。

  1. 使用path来匹配路由,通过query来传递参数,通过 this.$route.qurey.变量名 取值
  2. 使用name名来匹配路由,要使用params来传递参数,使 this.$route.params.变量名 取值
登录状态无需验证

在登录后保存token到本地存储sessionStorage,下次进入时判断是否有token

methods: {
    go() {
        const { data: res } = await this.$axios.post('login', this.userInfo)
        if (res.meta.status === 200) {
            // 提示登陆成功
            this.$message.success(res.meta.msg)
            // 保存token状态
            window.sessionStorage.setItem('token', res.data.token)
            // 跳转
            this.$router.push('/home')
          } else {
            this.$message.error('登录失败')
          }
        }
      },
     
     mounted() {
      if (window.sessionStorage.getItem('token')) {
        this.$router.push('/home')
      }
    }
上传图片
<template>
<el-tab-pane label="商品图片" name="3">
            <el-upload class="upload-demo" :action="uploadURL" :on-preview="handlePreview" :on-remove="handleRemove" :file-list="fileList" list-type="picture" :on-success="handleSuccess" :headers="headerObj">
              <el-button size="small" type="primary">点击上传</el-button>
              <template #tip>
                <div class="el-upload__tip">
                  只能上传 jpg/png 文件,且不超过 500kb
                </div>
              </template>
            </el-upload>
</el-tab-pane>
</template>
export default {
  data() {
    return {
        goodsValue: {
             goods_name: '',
             goods_price: 0,
             pics: [],
           },
      previewPath: '',
      previewVisible: false,
       fileList: [],
       // 地址
      uploadURL: 'http://127.0.0.1:9000/api/private/v1/upload',
      // 请求头
      headerObj: {
        Authorization: window.sessionStorage.getItem('token')
      }
           }
        }
      
// 上传图片开始
    // 预览
    handlePreview(file) {
      console.log('图片预览--------------', file)
      this.previewPath = file.response.data.url
      this.previewVisible = true
    },
    // 移除图片
    handleRemove(file) {
      // console.log(file)
      // 1. 获取将要删除的图片的临时路径
      const filePath = file.response.data.tmp_path
      // 2. 从 pics 数组中,找到这个图片对应的索引值
      const i = this.goodsValue.pics.findIndex(x => x.pic === filePath)
      // 3. 调用数组的 splice 方法,把图片信息对象,从 pics 数组中移除
      this.goodsValue.pics.splice(i, 1)
      console.log('移除图片--------------', this.goodsValue)
    },
    // 上传成功
    handleSuccess(response) {
      console.log('上传图片--------------', response)
      // 1. 拼接得到一个图片信息对象
      const picInfo = { pic: response.data.tmp_path }
      // 2. 将图片信息对象,push 到pics数组中
      this.goodsValue.pics.push(picInfo)
      console.log(this.goodsValue)
    }
    }
    // 上传图片结束
组件传递
  • 父传子: 父组件动态绑定一个属性,子组件通过props方式接受这个属性作为数据
  • 子传父:子组件发射一个事件$emit,父组件接受事件以后调用相应的函数
  • 兄弟相传: 使用一个空的Vue对象在第一个组件的点击事件中,使用emit方式来发射事件,在第二个组件的mounted函数里面使用emit方式来发射事件,在第二个组件的mounted函数里面使用on来接收事件
//父组件
<amend-user @updatelist="getUserList" :user="currentUser" ref="amendRef"></amend-user>

//子组件
export default {
props: ['user'],
methods: {
saveUserInfo() {
      this.$refs.amendUserRef.validate(async valid => {
        if (valid) {
          const { data: res } = await this.$axios.put(`users/${this.user.id}`, this.user)
          if (res.meta.status === 200) {
            this.$emit('updatelist')
          } 
        }
      })
    }
    }
    }
  • 在echart图标部分用到了lodash工具lodash.merge()将两组数据合并
项目优化
  1. 添加进度条

nprogress --- npm install nprogress -S

api中导入nprogress和nprogress/nprogress.css,在拦截请求和响应请求时分别使用nprogress.start()nprogress.done()

  1. 修改警告提示

buid--运行

template用的slot应为v-slot:slot名="scope"(如果没有用scope就空串)

  1. 去除打包的console.log

发布阶段 在打包阶段去掉console.log()

npm install babel-plugin-transform-remove-console -D

由于开发阶段不用去除,所以

在babel.config.js中配置plugins

const prodPlugins=[]
if(process.env.NODE_ENV==='production'){
prodPlugins.push('transform-remove-console')
}
module.exports={
plugins:[
...prodPlugins
]
}
  1. 完成打包多入口

vue.config.js中将开发阶段和发布阶段的打包入口分开

先分别创建main-prod.js和main-dev.js文件,在vue.config.js中配置如下代码:

module.exports = {
    chainWebpack:config=>{
        //发布模式
        config.when(process.env.NODE_ENV === 'production',config=>{
            //entry找到默认的打包入口,调用clear则是删除默认的打包入口
            //add添加新的打包入口
            config.entry('app').clear().add('./src/main-prod.js')
        })
        //开发模式
        config.when(process.env.NODE_ENV === 'development',config=>{
            config.entry('app').clear().add('./src/main-dev.js')
        })
    }
}
  1. 加载CDN

1)vue.config.js中

config.set('externals', {
        vue: 'Vue',
        'vue-router': 'VueRouter',
        axios: 'axios',
        echarts: 'echarts',
        nprogress: 'NProgress',
        moment: 'moment'
      })

2)需要在index.html页面中添加CDN

但在开发阶段不需要加载CDN,所以在vue.config.js中配置

config.plugin('html').tap(args => {
        args[0].isProd = true
        return args
      })

然后在index.html中用<%%>包裹script路径

<% if(htmlWebpackPlugin.options.isProd) { %>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.2/index.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.1.2/echarts.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.min.js"></script>
    <% } %>

需要注意的是要版本对应

6. 开启gzip压缩(服务器和前端都要进行配置)

这里有一个问题是 compression-webpack-plugin 使用版本不能太高

开启nginx部署: nginx 进行部署前端工程,nginx需要开启gzip压缩功能

nginx 给浏览器返回的gzip是实时压缩的,会造成nginx性能消耗,所以应该直接在nginx上放gzip,即前端打包的时候,直接打包成gzip文件

操作:

  • 下载compression-webpack-plugin

  • vue.config.js中配置

const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i
// gzip打包
  configureWebpack: {
    plugins: [
      new CompressionWebpackPlugin({
        filename: '[path].gz[query]',
        algorithm: 'gzip',
        test: productionGzipExtensions,
        threshold: 10240,
        minRatio: 0.8,
        deleteOriginalAssets: true
      })
    ]
  },
  1. 路由懒加载
  • 没有指定webpackChunkName,每个组件打包成一个js文件 const 组件名=() => import('组件路径');
  • 分组加载 把组件按组分块,指定了相同的webpackChunkName,会合并打包成一个js文件 const 组件名 = () => import(/* webpackChunkName: '组名' */ '组件路径') 分组加载需先下载syntax-dynamic-import插件,在babel.config.js里将'@babel/plugin-syntax-dynamic-import'放入plugins中

项目优化完毕,dist文件夹里会有一个gzip文件,最后将dist文件夹放到服务器上