VUE项目开发需要注意的

146 阅读9分钟

搭建VUE项目

  1. 使用 vue-cli 脚手架搭建初始项目
  2. 删除与项目不相关的文件及图标
  3. 创建远程仓库,关联仓库地址
  4. 创建开发中需要的文件夹:
    • views:用于存放路由组件,每个路由组件也对应有自己的文件夹
    • utils:存放功能性 js 文件
    • store:vuex管理文件
    • layout:存放菜单文件
    • router:路由管理文件
    • mixin:管理混入功能 js 文件
    • ...
  5. 如果需要配置 webpack,在根目录下创建 vue.config.js 文件来添加和修改配置
  6. 引入 axios 封装请求api
  7. 引入 elementUi 组件库
  8. 引入 样式预处理语言
  9. 引入 Vuex,配置基本文件
  10. 引入 vue-router 配置路由
  11. 重置公共样式(一些标签组件的默认样式)

vue脚手架配置代理处理跨域问题

  • 代理就是在客户端与真实服务器之间,再建立一个中转的服务器,这个服务的访问地址与客户端同源,就不会产生跨域问题,而服务器与服务器之间是不受同源策略影响的
  • 在vue.config.js中添加如下配置:
  • 方法一:
    • 在vue.config.js文件中配置 devServer:{proxy:"http://localhost:5000"}
    • 但这种方式只能配置一个代理地址,不够灵活
    • 这种代理会先向自身寻找
// vue.config.js 文件
module.exports = {
    pages: {
        index: {
              //入口
              entry: 'src/main.js',
        },
    },
    lintOnSave:false, //关闭语法检查
    
    //开启代理服务器(方式一)
    /* devServer: {
        proxy: 'http://localhost:5000'
    }, */
    
    //开启代理服务器(方式二)
    devServer: {
        proxy: {
            // '/api' 这个是前端自己定义的,用来识别。
            // 这个自定义的前缀,要紧跟在端口号之后
            // 当请求的api地址携带 ‘/api’,就进行代理访问 target 配置的真实服务器
            // 但是‘/api’是我们自己配置,所有在访问服务器时就需要重写请求使用 pathRewrite 配置
            '/api': {
            target: 'http://localhost:5000',
            pathRewrite:{'^/api':''},
            // ws: true, //用于支持websocket
            // changeOrigin: true //用于控制请求头中的host值,true则返回代理的服务器域名端口,false为真实的客户端域名端口
        },
        '/demo': {
            target: 'http://localhost:5001',
            pathRewrite:{'^/demo':''},
            // ws: true, //用于支持websocket
            // changeOrigin: true //用于控制请求头中的host值
            }
        }
    }
}

vue-router 使用

  • 路由守卫
    • 全局前置路由守卫(beforeEach),在路由跳转前执行的回调,一般可以用来做权限跳转,判定允许跳转后一定要调用 next()
    • 全局后置路由守卫(afterEach)————初始化的时候被调用、每次路由切换之后被调用
    • 路由独享守卫(beforeEnter)在定义路由规则时配置,用法与全局前置路由守卫一致,没有独享的后置路由
    • 组件内的守卫
      • beforeRouteEnter(to, from, next) { // 在渲染该组件的对应路由被 confirm 前调用 // 不!能!获取组件实例this // 因为当守卫执行前,组件实例还没被创建 }, beforeRouteUpdate(to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例this}, beforeRouteLeave(to, from, next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例this}
// router/index.js
const router = new VueRouter({ // 创建路由实例对象
    mode: 'hash', // 路由模式值为 hash 或 history
    routes: [ // routes用于配置路由规则
        {
            name:'guanyu', // 路由标识,
            path:'/about', // 访问该路由的地址
            component: () => import('./About.vue'), // 要加载的组件,加载组件一般使用懒加载
            meta:{title: '关于', isAuth: true}, // 路由元信息,也是程序员可以配置信息的配置,例如配置是否需要校验,加载该路由的网页标签的title
            beforeEnter: (to, from, next) => {
                // ...
            }
        },
        ...
    ],
    scrollBehavior (to, from, savedPosition) {
    // return 期望滚动到哪个的位置
    }
})

// 全局前置路由
router.beforeEach((to, from, next) => {
  // ...
})

// 全局后置路由
router.afterEach((to,from)=>{
    console.log('后置路由守卫',to,from)
    document.title = to.meta.title || '系统'
})

export default router

路由器的两种工作模式

  1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
  2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
  3. hash模式:
    1. 地址中永远带着#号,不美观 。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性较好。
  4. history模式:
    1. 地址干净,美观 。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。

菜单权限管理 与 按钮权限管理

菜单权限管理
  1. 首先前端项目中需要有权限管理的菜单和对应的操作组件
  2. 依据RBAC(role based access control)权限设计的思想
  3. 首先可以创建和管理员工、角色
  4. 而所有 权限 由后端返回
  5. 员工可以赋予多个角色,而根据角色的特点可以分配多个不同的权限
  6. 根据员工赋予的角色,就可以知道该员工有什么权限,这些都会传给后端保存
  7. 根据员工登录,可以由后端返回员工所持有的权限
  8. 根据员工所持有的权限去动态添加路由,vue-router提供了一个叫做addRoutes的API方法,这个方法的含义是动态添加路由规则
  9. 后端会在返回的员工信息里面返回路由权限的标识,可以将路由模块的根节点name属性命名和权限标识一致,这样只要标识能对上,就说明用户拥有了该权限
  10. 通过筛选的路由再生成静态路由,再去渲染菜单,而路由由 addRoute进行添加
按钮权限管理
  1. 将每个按钮功能赋予权限标识。
  2. 后端会保存所有的权限标识,根据登录的员工账号返回拥有的权限
  3. 前端可以通过 自定义指令 或者 混入 的方式给每个需要权限校验的功能绑定
  4. 通过绑定的方法传入被绑定的功能的标识,通过返回值的布尔值结果,决定功能是否可以使用
  5. 可以通过隐藏或者禁用的方式去限制

Element-Ui 按需引入的问题

  • 在项目中一般为了让项目内存更小,会在使用UI组件库时,使用按需引入的方式。
  • 根据Element官网的配置会出现项目运行不起来
  • 第一,会提示各种包未安装,根据提示安装即可
  • 第二,将官方的配置文档做一些修改
// babel.config.js 文件
{
    // 这是官方提供的配置,跟项目中的合并即可
    "presets": [
        // ...原来的配置
        // 而这里的 es2015 就是报错的点,要修改为 @babel/preset-env
        // ["es2015", { "modules": false }]
        ["@babel/preset-env", { "modules": false }]
    ], 
    "plugins": [[ 
        "component", 
        { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" }
     ]]
}

项目中一些需要注意的

组件中 props 和 data 权重

  • 在一个组件中,如果父组件传入参数对象 props 中的一个属性 和组件本身的 data 返回值中的一个属性重名,这两者之间 props 的属性权重高,页面会渲染由父组件传入的 props 的属性。
  • 传入的 props 属性不能在子组件内部直接修改
  • 传入子组件的 props 参数属性名不能有关键字,例如:key属性、ref属性

scoped样式

  1. 作用:让样式在局部生效,防止冲突。
  2. 写法:<style scoped>

组件之间传值:自定义事件

  • 子组件给父组件传值:通过绑定自定义事件传递
    • 父组件使用子组件,在父组件中:<Demo @atguigu="test"/>
    • 子组件内部触发自定义事件:this.$emit('atguigu',数据)
    • 子组件内部解绑自定义事件this.$off('atguigu')
      • this.$off('atguigu') 传入一个字符串,则解除一个自定义事件
      • this.$off(['atguigu', 'abc']) 传入一个字符串数组,则解除多个自定义事件
      • this.$off() 什么都不传,则解除所有自定义事件
      • 在 beforeUnmount 生命周期去解除
  • 其他关系组件间传值
    • 使用事件总线方式
    • 使用消息订阅与发布,需要插件 pubsub,命令:npm i pubsub-js

自定义组件绑定原生DOM事件

  • 自定义组件上也可以绑定原生DOM事件,需要使用native修饰符。
  • <A @click.nativev="函数方法"></A>
  • native修饰符就会将 click 当成原生DOM事件,并将这个事件绑定到组件的最外层容器元素上
  • 如果没有加 native修饰,组件上就会将事件以自定义事件处理

nextTick

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

public 文件夹

  • 项目中存在一个文件夹:public,文件夹中有一个 index.html 文件,这个文件就整个应用的界面,也是模板
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <!-- 针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 开启移动端的理想视口 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!-- 配置页签图标 -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- <%= htmlWebpackPlugin.options.title %> 这一串会匹配到项目中 package.json 文件的name属性 -->
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    // 当浏览器不支持 JavaScript 时就执行 noscript
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

混入(mixin)

  • 将组件中具有相同处理功能的函数方法提取到一个文件中,通过暴露和引入的方式,去注册混入。达到每个混入的组件都可以实现这个功能。
  • 这样就不需要每个组件编写同样的代码,还可以统一管理。
  • 混入是通过混入组件中的配置项,相同的配置项会合并。(配置项例如:data,methods,mounted)
  • 如果混入的方法或属性与组件内部重复,则会以组件内部为主。
  • 混入生命周期配置,而组件内部也有相同的生命周期,则都会执行生命周期里面的代码
  • 特别注意混入相同的生命周期,则混入的代码先执行,组件内部的后执行
// 定义混入文件 myMixin.js,通过分别暴露的方式,想要使用哪个混入这引入那个
export const mixin1 = {
    methods: {
        showName(){
            alert(this.name)
        }
    },
    mounted() {
        console.log('你好啊!')
    },
}
export const mixin2 = {
    data() {
        return {
            name: 'hhh'
        }
    },
}

// 在组件中 局部混入
import {mixin1, mixin2} from '混入的文件路径'
// 在与 data 同级的配置 mixins 中注册混入
mixins:[mixin1, mixin2]

// 也可以全局混入
// 在项目的入口文件 main.js 中注册
import {mixin1, mixin2} from '混入的文件路径'
Vue.mixin(mixin1)
Vue.mixin(mixin2)
// 这时候,所有的组件包括根组件都混入了定义的配置

webStorage 浏览器储存

  1. 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
  2. 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
  3. 相关API:
    1. xxxxxStorage.setItem('key', 'value');该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
    2. xxxxxStorage.getItem('person');该方法接受一个键名作为参数,返回键名对应的值。
    3. xxxxxStorage.removeItem('key');该方法接受一个键名作为参数,并把该键名从存储中删除。
    4. xxxxxStorage.clear()该方法会清空存储中的所有数据。
  4. 备注:
    1. SessionStorage存储的内容会随着浏览器窗口关闭而消失。
    2. LocalStorage存储的内容,需要手动清除才会消失。
    3. xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。
    4. JSON.parse(null)的结果依然是null。