VUE开发篇

VUE开发

快速原型开发

可以使用 vue serve 和 vue build 命令对单个 .vue 文件进行快速原型开发。但是需要先安装@vue/cli-service-global插件。

npm install -g @vue/cli-service-global

新建一个.vue文件,然后启动一个服务并运行原型:

vue serve 文件名.vue

脚手架创建项目

官方地址

vue create
vue create my-vue

选择Default ([Vue 2] babel, eslint)就可以了。

如果想要自己配置,那么可以选择第三个:

默认还是选择了默认配置babel、linter,其它的根据自己需求选择。

enter下一步:(2.x版本和3.x版本的选择)

enter下一步进入EsLint配置:(很严格还是只是在命令行中给提示,建议选择默认的第一个)

enter下一步:(保存的时候做Lint还是提交的时候)

enter下一步:(配置文件的选择)

然后执行:

cd 项目名

yarn serve

然后浏览器中打开http://192.168.8.132:8080/就可以看到欢迎界面。

项目文件说明:

vue ui

图形化项目管理:

vue ui
安装插件

Vue CLI 使用了一套基于插件的架构。插件可以修改 webpack 的内部配置,也可以向 vue-cli- service 注入命令。在项目创建的过程中,绝大部分列出的特性都是通过插件来实现的。

在一个已经被创建好的项目中安装一个插件,可以使用 vue add 命令。例如安装路由插件:

vue add router

(然后会提示你现在有代码变的操作)

输入y继续:

然后再输入y,插件添加成功。

(添加插件,可能会对项目进行破坏性地结构上的变更,甚至是文件内容的修改。例如查看main.js和App.vue。所以其实最好把安装之前的版本给备份一下。)

处理资源路径

当你在 JavaScript、CSS 或 *.vue 文件中使用相对路径 (必须以 . 开头) 引用一个静态资源时,该资源将被webpack处理。

转换规则:

  • 如果 URL 是一个绝对路径 (例如 /images/foo.png ),它将会被保留不变。

    <img alt="logo" src="/assets/pyy.png"> // 会在public文件夹中去找,名字不会被改动:webpack不会对public做任何操作
    <img alt="pyy" src="https://www.baidu.com/xxx/pyy.png"> // 图片服务器中的地址访问 
    
  • 如果 URL 以 . 开头会作为一个相对模块请求被解释并基于文件系统相对路径。

    // 会被webpack编译(打开检查器查看图片src,我们可以看到最终编译的结果是文件的hash)
    // 关于图片webpack做的操作:  图片都会有批量的处理,把它作为资源、模块来做处理,如果足够小,会把它变成url路径,做字符串的编码。如果足够大,会起一个合适的名字,放到一个合适的地方去。 
    <img alt="logo" src="./assets/pyy.png">
    
  • 如果 URL 以 @ 开头会作为一个模块请求被解析。Vue CLI 默认会设置一个指向 src 的别名 @ 。

    <img alt="logo" src="@/assets/pyy.png"> // @代表src根目录
    
  • 扩展:如果 URL 以 ~ 开头会作为一个模块请求被解析。这意味着甚至可以引用 Node 模块中的资源(node_modules中的)

    <img src="~xxx-npm-package/pyy.png">
    

那么何时使用public文件夹呢?

通过 webpack 的处理并获得如下好处:

  • 脚本和样式表会被压缩且打包在一起,从而避免额外的网络请求。

  • 文件丢失会直接在编译时报错,而不是到了用户端才产生 404 错误。

  • 最终生成的文件名包含了内容哈希,因此不必担心浏览器会缓存它们的老版本。

使用public的场景:

  • 需要在构建输出中指定一个固定的文件名字。

  • 有非常多的图片,需要动态引用它们的路径。

  • 需要考虑兼容性,使用script标签引入。

注意:

如果应用打包上传到服务器之后,没有部署在域名的根部,那么需要配置publicPath前缀:

在项目最外层,加上一个vue.config.js文件,文件中添加如下代码:

module.exports = { 
	// 判断当前运行环境 是生产环境还是开发环境 
	// 生产环境时加上/部署服务器中的文件夹名/, 开发环境时就不添加 
  publicPath: process.env.NODE_ENV === 'production'? '/部署服务器中的文件夹名/': '/' 
}
预处理器

如果创建项目时没有选择需要的预处理器(Sass/Less/Stylus),则需手动安装相应loader。

# Sass 
yarn add sass-loader node-sass 
# Less 
yarn add less-loader less 
# Stylus 
yarn add stylus-loader stylus

安装完成了,就可以使用了。

<style scoped lang='scss'>
  $color: green; 
  h1 { 
    color: $color; 
  }
</style>

注意:当 <style> 标签有 scoped 属性时,它的 CSS 只作用于当前组件中的元素,其原理是通过使用PostCSS 来实现转换。

<template> 
	// 给标签添加自定义属性 data-v-f3edg9
	<div class="green" data-v-f3edg9>hi</div> 
</template> 

<style> 
	// 标签添加的自定义属性和postcss来对应
	.green[data-v-f3edg9] { 
		color: green; 
	}
</style>

扩展1:

如果我们很多组件都需要使用scss变量,那么怎么办呢,这个时候我们可以把公共的提出去。例如我们上面的$color: green变量给提出去。

在src下新建一个styles文件夹,然后添加一个common.scss文件,把$color: green剪切到这个文件中。

然后装style-resources-loader 插件。

yarn add style-resources-loader

然后在vue.config.js文件中,修改代码为:

const path = require('path')

function addStyleResource(rule) {
  rule.use('style-resource')
    .loader('style-resources-loader')
    .options({
      patterns: [
        path.resolve(__dirname, './src/styles/common.scss'),
      ],
    })
}
module.exports = { 
  publicPath: process.env.NODE_ENV === 'production'? '/部署服务器中的文件夹名/': '/',
  chainWebpack: config => {
    const types = ['vue-modules', 'vue', 'normal-modules', 'normal']
    types.forEach(type => addStyleResource(config.module.rule('scss').oneOf(type)))
  }, 
}

然后再重新启动项目。

在程序启动之初,先把这个文件加载了,然后其他文件就都可以使用了。

扩展2:

深度作用选择器: >>>

在非css预处理器中,使用 >>> 操作符可以使 scoped 样式中的一个选择器能够作用得“更深”,例如影响

子组件(必需要写在scoped 样式中)。

<style scoped> 
	#app >>> p { 
		color: green 
	}
</style>

在css预处理器无法正确解析 >>> 。我们使用 /deep/ 或 ::v-deep 操作符。

<style scoped lang="scss"> 
	 #app {
    // /deep/ p{
    //   color: green;
    // }
    ::v-deep p{
      color: green;
    }
  }
</style>

路由

官方地址

安装

如果之前创建项目没有选择router,那么需要安装路由插件。

vue add router
基础使用

在router/index.js,可以配置路由。

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    // 使用webpack+vue提供的异步组件的方式实现懒加载
    // 代码将来会分割出去成一个独立的chunk(分片的模式)
    // 只有进入到这个路由,才会实时的去下载这个组件,因为打包的时候是单独打包出去的,这样加载时间会变短
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]
路由出口

一般在App.vue中,使用路由的总出口 : <router-view></router-view>

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>

    <!-- 路由的出口 -->
    <router-view/>
  </div>
</template>
动态路由匹配

我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 Detail 组件,对于所有 ID 各不相同的商品,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:

{ path: '/detail/:id', component: Detail }
 // router/index.js中添加:
 {
    // :id标示动态路由
    path: '/detail/:id',
    name: 'Detail',
    component: () => import('../views/Detail.vue')
  },
路由导航

语法:<router-link :to='' />

使用router-link可以进行路由导航,to是要导航到哪儿去,里面写的是路由的path。

使用:

      <li v-for="item of goodsList" :key='item.id'>
        <router-link :to='`/detail/${item.id}`'>
          {{item.name}} - {{item.price | symbol("$")}}
        </router-link>
      </li>

获取:

<template>
  <div>
    <h1>商品详情页</h1>
    <p>{{$route.params.id}}</p>
  </div>
</template>

<script>
  export default {
  	// 响应路由参数变化
    // 如果不这样监听,那么在多个之间切换的时候,组件为了考虑性能,是不会进行销毁重建的,也就是说在下面mounted或者created都不会再触发
    watch: {
      $route: {
        immediate: true,
        // deep: true,
        handler() {
          console.log('通过id去获取详情');
          
        }
      }
    },
    mounted () {
      console.log(this.$route.params.id);
    },
     created () {
      console.log('发起请求');
    },
  }
</script>
通配符
	{
    // 会匹配所有路径
    // 从上往下去匹配,当其他的都匹配不上,代表路由有误,这个时候就可以去加载我们写的404提示页面
    // 一般情况下,都会放到最后
    path: '*',
    component: () => import('../views/404.vue')
  }
嵌套路由

实际的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件。

在路由对象中添加children属性,值是一个数组对象。

 {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue'),
    children: [
      {
        path: 'detail/:id',
        name: 'Detail',
        component: () => import('../views/Detail.vue')
      }
    ]
  },

改造about页面添加嵌套路由出口:

<template>
  <div class="about">
    <h1>about</h1>
    <ul>
      <li v-for="item of goodsList" :key='item.id'>
        <router-link :to='`/about/detail/${item.id}`'>
          {{item.name}} - {{item.price | symbol("$")}}
        </router-link>
      </li>
    </ul>

    <!-- 嵌套路由出口 -->
    <router-view></router-view>
  </div>
</template>

这个时候,(1)访问/detail/:id,就是单独的详情页面,(2)/about是商品列表,(3)/about/detail/:id是商品列表下还有商品详情。

路由跳转

语法:router.push(location, onComplete?, onAbort?)

// 字符串 
router.push('/') 
// 对象 
router.push({ path: `/about/detail/${item.id}` }) 
// 命名的路由 
router.push({ 
	name: 'Detail', // 路由文件中,去匹配 我们书写的name值
	params: { id: item.id } // 参数
})
// 带查询参数,最终会变成 /goods?price=100 
router.push({ path: 'goods', query: { price: 100 }})
路由懒加载

路由组件的懒加载能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

把组件按组分块:

() => import(/* webpackChunkName: "group-about" */ "../views/About.vue")

路由进阶

路由守卫

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。

全局守卫
router.beforeEach((to, from, next) => { 
	xxx代码 
	// to: Route: 即将要进入的目标 路由对象 
	// from: Route: 当前导航正要离开的路由 
	// next: Function: 一定要调用该方法来 resolve 这个钩子。 
})

在router/index.js中,添加

// 全局守卫
// to:到哪儿去 from:从哪儿来 next:放行
router.beforeEach((to, from, next) => {
  // 判断路由是否需要守卫
  // meta数据
  if (to.meta.auth) {
    // 是否登录
    if (window.isLogin) { // 假如在全局保存了一个变量isLogin,用来判断是否登陆
      next() // 放行
    } else {
      next('/login?redirect='+to.fullPath) // 跳转到登陆,然后登陆成功之后重定向到想去的页面
    }
  } else {
    next()
  }
})
路由独享的守卫

和全局守卫beforeEach参数是一致的。

beforeEnter((to, from, next) => { 
	xxx代码 
	// to: Route: 即将要进入的目标 路由对象 
	// from: Route: 当前导航正要离开的路由 
	// next: Function: 一定要调用该方法来 resolve 这个钩子。 
})

例如:

 {
    path: '/about',
    name: 'About',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
    children: [
      {
        path: 'detail/:id',
        name: 'Detail',
        component: () => import('../views/Detail.vue')
      }
    ],
    beforeEnter(to, from, next) {
      // 是否登录
      if (window.isLogin) { // 假如在全局保存了一个变量isLogin,用来判断是否登陆
        next() // 放行
      } else {
        next('/login?redirect=' + to.fullPath) // 跳转到登陆,然后登陆成功之后重定向到想去的 页面
      }
    }
  },
组件内守卫

可以在路由组件内直接定义以下路由导航守卫(写在组件内的方法):

  • beforeRouteEnter

  • beforeRouteUpdate

  • beforeRouteLeave

// About.vue 
beforeRouteEnter(to, from, next) { 
	if (window.isLogin) { 
		next(); 
	} else { 
		next("/login?redirect=" + to.fullPath); 
	} 
}

动态路由

通过router.addRoutes(routes)方式动态添加路由

例如很常见的要求用户必须登录,否则只能去登录页。

修改全局守卫为:

router.beforeEach((to, from, next) => {
  // 已登录
  if (window.isLogin) {
    if (to.path === '/login') {
      // 已登录没必要去登录页,重定向至首页
      next('/')
    } else {
      // 去其他页放行
      next()
    }
  } else {
    // 没有登录
    if (to.path === '/login') {
      // 要去登录页就直接放行
      next()
    } else {
      // 否则重定向到登录页
      next('/login?redirect=' + to.fullPath)
    }
  }
})

修改Login.vue,当用户登录成功后动态添加/about

		login() {
      window.isLogin = true;
      // 注意参数需要是数组对象,如果没有登录,那么就不会有这个路由
      this.$router.addRoutes([{
        path: "/about",
        name: "About",
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        // 使用webpack+vue提供的异步组件的方式实现懒加载
        // 代码将来会分割出去成一个独立的chunk(分片的模式)
        // 只有进入到这个路由,才会实时的去下载这个组件,因为打包的时候是单独打包出去的,这样加载时间会变短
        component: () =>
          import(/* webpackChunkName: "about" */ "../views/About.vue"),
        children: [
          {
            path: "detail/:id",
            name: "Detail",
            component: () => import("../views/Detail.vue")
          }
        ],
        beforeEnter(to, from, next) {
          // 是否登录
          if (window.isLogin) {
            // 假如在全局保存了一个变量isLogin,用来判断是否登陆
            next(); // 放行
          } else {
            next("/login?redirect=" + to.fullPath); // 跳转到登陆,然后登陆成功之后重定向到想去的 页面
          }
        }
      }]);
      
       // 使用的是带查询参数,所以是获取query
      this.$router.push(this.$route.query.redirect);
    }

然后,来思考一下,虽然做了权限认证,但是会遇到一个问题,当我刷新的时候,添加的路由没了,又要走登录流程,这个肯定是不合理的,那么怎么来解决呢?自己试一试呢。

组件缓存

keep-alive

利用keepalive做组件缓存,保留组件状态,提高执行效率。

例如:缓存about组件。

在App.vue中,改造代码:

		<!-- 缓存组件 -->
    <keep-alive include="about" max='5'>
    	<!-- 注意使用include或则exclude时,去匹配的是组件中的name值,不是路由中的name -->
    	<!-- include是要缓存的组件,exclude是不缓存的组件,如果是多个,name值之间逗号隔开,例如include="about,detail" -->
    	<!-- 属性max,是指需要缓存的最大数,如果超出了,就会把最老的缓存t出去,加上最新的缓存, 以达到资源的最优化 -->
      <router-view/>
    </keep-alive>

由于keep-alive的出现,就有两个特别的钩子:activated、deactivated。

在about组件中去监听:

  // 被keep-alive缓存会触发
  activated(){
    console.log('activated');
  },
  deactivated(){
    console.log('deactivated');
  },

github代码地址

原文地址:https://www.cnblogs.com/zz-zrr/p/14438076.html