前端学习篇 -- Vue 全家桶

1、Vue-Cli

1.1 常用命令
1.1.1 安装
  1. 安装 vue-cli3 以上版本

    npm i @vue/cli -g

  2. 拉取 vue-cli2 模板

    npm i @vue/cli-init

上面安装好之后,就既可以使用 vue-cli2 ,也可以使用 vue-cli3

1.1.2 创建项目
  1. vue-cli3 以上版本

    vue create my-project --> my-project 为项目名

    Please pick a preset: //配置选择
      > default  //默认
      > Manually select features //手动选择 *
        #空格选择配置项
    Where do you prefer placing...? //配置项保存选择
      > In dedicated config files //保存为独立文件 *
      > In package.json //保存到package.json
    Save this as a preset for futrue projects? (y/N) //是否保存配置项,下次创建项目时,第一项就有三个选项
    
    • 配置项选择
    Babel : 将ES6编译成ES5
    TypeScript: javascript类型的超集
    Progressive Web App (PWA) Support: 支持渐进式的网页应用程序
    Router:vue-router
    Vuex: 状态管理
    CSS Pre-processors: CSS预处理
    Linter / Formatter: 开发规范
    Unit Testing: 单元测试
    E2E Testing: 端到端测试
    
  2. vue-cli2

    vue init webpack my-project --> my-project 为项目名

    Project name my-project --> `项目名,回车
    Project description A Vue.js project --> `描述信息,回车
    Author lxc <871673791@qq.com> --> `作者,回车
    Vue build <Use arrow keys>
      > `Runtime + Compiler --> 代码中可以有 template,回车
      > `Runtime-Only --> 项目文件比上面的轻,一般项目开发使用这个
    Install vue-router? (Y/n) --> `是否安装路由
    Use ESLint to lint your code? (Y/n) --> `是否启用代码规范
      > `Standard //标准规范
      > `Airbnb //爱彼迎规范
      > `none //自定义规范
    to invert selection >
    >(*) Lint on save // 保存就检测 (我一般选择它)
     ( ) Lint and fix on commit // fix和commit检查
    Set up unit tests (Y/n) --> `是否单元测试,n
    Setup e2e tests with Nightwatch? (Y/n) --> `是否端到端测试,n
    >>>>> 安装完毕
    
1.2 eslint 关闭

有时候,配置了 eslint 代码规范,反而给我们写代码造成了困扰

config>index.js>useEslint:false
1.3 vue-cli 3 与 2 区别
  1. 3 是基于 webpack 4 打造,2 是基于webpack 3
  2. 3 的设计原则是 “0配置”,移除根目录下的 build 和 config 等目录
  3. 3 提供了 vue ui 命令,提供了可视化配置
  4. 3 移除了 static 文件夹,新增了 public 文件夹,并将 index.html 移动到里面

2、Vue-Router

路由就是通过互联的网络把信息从源地址传输到目的地地址的活动

核心就是:改变 url,但是页面不进行整体刷新

2.1 方案
  1. 改变 url 的 hash

    • url 的 hash 也就是锚点(#),本质上是改变window.location的href属性

      location.hash = '/foo'
      // url/#/foo
      
  2. HTML5的history模式:pushState

    history.pushState({},'','foo')
    // url/foo
    
    // 这种可以保留历史记录,调用 history.back() 方法可以后退
    
    history.replaceState({},'','foo')
    // 这种是替换当前页面,不能前进后退
    
2.2 安装和使用
  1. 安装

    npm i vue-router
    
  2. 在模块化工程中使用它

    因为是一个插件,所以可以通过Vue.use()来安装路由功能

    第一步:导入路由对象,并且调用 Vue.use(VueRouter)

    第二步:创建路由实例,并且传入路由映射配置

    第三步:在Vue实例中挂载创建的路由实例

    // router>index.js
    // 1.导入路由对象
    import Router from 'vue-router'
    import Vue from 'vue'
    
    // 2.通过 Vue.use(插件),安装插件
    Vue.use(Router)
    
    // 3.创建 Router 对象
    const routes = [
    
    ]
    
    const router = new Router({
      // 配置路由和组件
     routes,
      // 将默认hash模式调成history模式
      mode: 'history',
      // 将 router-link 的类名统一修改
      linkActiveClass: 'active'
    })
    
    // 4.将 router 对象传入到 vue 实例
    export default router
    
    // main.js
    import App from './App.vue'
    import router from './router'
    
    new Vue({
      el: '#app',
      router,
      render: h => h(App)
    })
    
2.3 配置简单路由
  1. <router-link></router-link>
    vue-router 里已经配置好的组件,最终被渲染成 a标签
  2. <router-view></router-view>
    占位,组件渲染位置
// index.js
import Home from '../components/home'
import About from '../components/about'
//注册路由
const routes = [
  {
    path: '',
    //重定向,第一次显示首页
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]

const router = new Router({
  routes,
  mode: 'history'
})
<div id="app">
  <router-link to="/home">首页</router-link>
  <router-link to="/about">关于</router-link>
  <router-view/>
</div>
2.4 router-link 属性
  1. to 属性: 对应注册的 path 路径

  2. tag 属性: 将 router-link 渲染成对应的标签

    <router-link tag ="button"></router-link>
    
  3. replace 属性:换成 history. replaceState 替换当前页面,没有前进后退

  4. active-class 属性:选中的标签默认有两个 class 类名

    router-link-exact-active 和 router-link-active

    • 如果要改,直接赋值 active-class="active",项目中不建议改

    • 统一修改

2.5 动态路由

根据不同的用户信息显示不同的页面,传递参数主要有两种类型:

2.5.1 params的类型
  1. 配置路由格式:/router/:id
  2. 传递的方式:在 path后面跟上对应的值
  3. 传递后形成的路径:/router/123,/router/abc

第一步:注册组件

<router-link :to="'/user/'+userId">用户</router-link>
data() {
  return {
    userId: 'lisi'
  }
}

第二步:配置路由

{
  path: '/user/:userId',
  component: User
}

第三步:渲染用户信息

<div class="user">我是用户{{ userId }}</div>
computed: {
  userId() {
    return this.$route.params.userId
  }
}

我是用户lisi

匹配模式匹配路径$route.params
/users/:username/users/eduardo{ username: ‘eduardo’ }
/users/:username/posts/:postId/users/eduardo/posts/123{ username: ‘eduardo’, postId: ‘123’ }
2.5.2 query的类型
  1. 配置路由格式:/router,也就是普通配置
  2. 传递的方式:对象中使用querykey作为传递方式
  3. 传递后形成的路径:/router?id=123,/router?id=abc

第一步:注册组件

<router-link :to="{path:'/profile',query:{name:'zhangsan'}}">档案</router-link>

第二步:配置路由

{
  path: '/profile',
  component: Profile
}

第三步:渲染用户信息

<div>
  <h2>{{$route.query.name}}</h2>
</div>

zhangsan

2.5.3 编程式导航
// 字符串路径
router.push('/users/eduardo')

// 带有路径的对象
router.push({ path: '/users/eduardo' })

// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })

// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })

// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })

注意:如果提供了 path,params 会被忽略。需要提供路由的 name 或手写完整的带有参数的 path :

const username = 'eduardo'
// 我们可以手动建立 url,但我们必须自己处理编码
router.push(`/user/${username}`) // -> /user/eduardo
// 同样
router.push({ path: `/user/${username}` }) // -> /user/eduardo
// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
router.push({ name: 'user', params: { username } }) // -> /user/eduardo
// `params` 不能与 `path` 一起使用
router.push({ path: '/user', params: { username } }) // -> /user
2.6 路由的懒加载(分离打包)
  1. 当打包构建应用时,JavaScript 包会变得非常大,影响页面加载
  2. 不同的组件分割成不同的代码块,当路由被访问时再加载对应的组件

方式

一:结合Vue的异步组件和Webpack的代码分析

const Home = resolve => {
  require.ensure(['../components/home.vue'], () => {
    resolve(require('../components/home.vue'))
  })
}

二:AMD写法

const Home = resolve => require(['../components/home.vue'], resolve)

三:ES6写法

const Home = () => import('../components/home.vue') *
const routes = [
  {
    path: '/home',
    component: Home
  }
]

or

const routes = [
  {
    path: '/home',
    component: () => import('../components/home')
  }
]
2.7 命名视图

可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default。

<router-view class="view left-sidebar" name="LeftSidebar"></router-view>
<router-view class="view main-content"></router-view>
<router-view class="view right-sidebar" name="RightSidebar"></router-view>

一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置 (带上 s):

const routes = [
  {
    path: '/',
    components: {
      default: Home,
      // LeftSidebar: LeftSidebar 的缩写
      LeftSidebar,
      // 它们与 `<router-view>` 上的 `name` 属性匹配
      RightSidebar,
    }
  }
]
2.8 导航守卫

在一个SPA应用中,监听路由的跳转过程,做一些操作,比如更改网页的 title标题

2.8.1 全局守卫
  • beforeEach:前置守卫(guard),路由跳转之前回调,必须要调用next()函数

    • to:即将要进入的目标路由
    • from:即将要离开的路由对象
    • next:调用该方法,才能进入下一个钩子
  • beforeResolve: 解析守卫,和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用

  • afterEach:后置钩子(hook),路由跳转之后回调,不需要再调用next()函数

    router.afterEach((to, from) => {
      ...
    })
    

index.js

第一步:路由文件定义函数

router.beforeEach((to,from,next) => {
  document.title = to.matched[0].meta.title
  next()
})

// 或者
router.afterEach((to,from) => {
  document.title = to.matched[0].meta.title
})

第二步:定义元数据(描述数据)

{
  path: '/about',
  component: About,
  meta: {
    title: '关于'
  }
}
2.8.2 路由独享的守卫
  • 直接在某个路由配置上定义beforeEnter守卫,参数跟beforeEach一样

    • to:即将要进入的目标路由
    • from:即将要离开的路由对象
    • next:调用该方法,才能进入下一个钩子
    {
      path: '/about',
      component: About,
      meta: {
        title: '关于'
      },
      beforeEnter: (to,from,next) => {
        console.log('beforeEnter')
        next()
      }
    }
    
2.8.3 组件内的守卫
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

3、Vuex

一个专门为 Vue.js 应用程序开发的状态管理模式

  • 如用户的登录状态、用户信息
  • 如商品的收藏、购物车的物品等等
3.1 作用流程

组件引用(Render)vuex中的state,发出(Dispatch)一个行为(Actions),提交(Commit)到 Mutations,再来改state

Devtools可以记录修改的状态,但是必须是通过Mutations修改的,可以不经过Actions直接修改,但是必须是同步操作,Devtools只能跟踪同步,异步操作只能通过Actions,再提交到Mutations就是同步了

3.2 安装使用
  1. 安装

    npm i vuex
    
  2. 使用

    // store 文件夹 index.js
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    const store = new Vuex.Store({
      state: {
      
      }
    })
    
    export default store
    
    // main.js   
    import store from './store'
    new Vue({
      el: '#app',
      store,
      render: h => h(App)
    })
    
3.3 Vuex 五个核心概念

如果状态信息是保存到多个 Store 对象中,那么之后的管理和维护都会非常困难,所以 vuex 使用了单一状态树管理应用层级的全部状态

响应式原理

Store 对象中的属性都会被加入到响应式系统中,而响应式系统会监听属性的变化,当属性发生变化时,通知所有界面用到该属性的地方发生刷新

  • 所有响应式属性必须是先在 store定义好的
  • 后面追加的属性如果要响应式,须通过 Vue.set() 方法追加
3.3.1 state

单一状态树(数据源),放状态相关的信息,在项目中任何位置可以通过$store.state访问其里面的属性

mapState 辅助函数
// store/index.js
const store = new Vuex.Store({
  state: {
    count: 10
  }
})
import { mapState } from 'vuex'
// 映射 this.count 为 this.$store.state.count
computed: {
  ...mapState({
    count: 'count' // 这里可以重命名
  })
  // 或者
  ...mapGetters(['count']} // 这里不能重命名
}
3.3.2 getters

类似计算属性,有时候需要获取 state编译后的状态

const store = new Vuex.Store({
  state: {
    count: 10
  },
  /**
   * getters 里的方法第一个参数指向 state 状态
   * getters 里的方法第二个参数指向 getters 状态
   */
  getters: {
    more(state) {
      return state.count * 10
    },
    moreTwo(state, getters) {
      return getters.more * 10
    },
    //可以直接返回一个函数让用户操作
    backCount(state) {
      return count => {
        state.count = count
      }
    }
  }
})

// App.vue
<p>{{$store.getters.more}}</p>
<p>{{$store.getters.moreTwo}}</p>
// 此处把 count 改成了 20
<p>{{$store.getters.backCount(20)}}</p>
// 200
// 2000
// 20
mapGetters 映射

store中的getters映射到局部计算属性

// store.js
export default {
  state: {
    cartList: []
  },
  getters: {
    cartCount(state) {
      return state.cartList.length
    }
  }
}

import { mapGetters } from 'vuex'
// 把 `this.cartCount` 映射为 `this.$store.getters.cartCount`
computed: {
  ...mapGetters({
    cartCount: 'cartCount' // 这里可以重命名
  })
  // 或者
  ...mapGetters(['cartCount']} // 这里不能重命名
}
3.3.3 mutations

更新 store 状态的唯一方式:提交(commit)mutations

里面的方法必须是同步函数,同步操作可以在组件内直接 commitmutations

普通提交

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  //mutations 里的方法参数 state 指向 state 状态
  mutations: {
    increment(state) {
      state.count++
    },
    decrement(state) {
      state.count--
    }
  }
})

// 3.导出
export default store
// 提交一个行为到 mutations
this.$store.commit('increment')

参数被称为mutation的载荷(payload),也可以是一个对象

普通传参

this.$store.commit('incrementCount', 5)

// mutations.js
incrementCount(state, count) {
  state.count += 5
}

传对象参数

// App.vue
this.$store.commit({
  type: 'incrementCount',
  count: 5
})

// mutations.js
incrementCount(state, payload) {
  return state.count += payload.count
}
类型常量

避免mutation中方法名出错,官方推荐使用类型常量

// store/mutation-types.js    
export const INCREMENT = 'increment'

// index.js 
import { INCREMENT } from './mutation-types'
mutations: {
  [INCREMENT](state) {
    ...
  }
}

mapMutations 映射

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
    ]),
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}
3.3.4 actions

处理异步操作,可以接收两个参数,conrext表示上下文,payload表示接收数据,可以是一个对象

// store > index.js
state: {
  count: 0
},
mutations: {
  increment (state) {
    state.count++
  }
},
actions: {
  increment (context) {
    context.commit('increment')
  }
}

// App.vue
this.$store.dispatch('increment')
mapActions 映射
// actions.js
export default {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}
import { mapActions } from 'vuex'

methods: {
  ...mapActions(['actionA']),
  // this.addCart(product).then(res => console.log(res)) 等同于
  // this.$store.dispatch('addCart', product).then(res => console.log(res))
}
3.3.5 modules
  • Vue 使用单一状态树,那么意味着很多状态都会交给 Vuex 来管理
  • 当应用变得复杂时,stote 对象就有可能变得相当臃肿
  • 为了解决这个问题,可以将 store 分割成模块(Module),而每个模块都拥有自己的 stategettersmutationsactions
用法
const moduleA = {
  state: {
    name: 'zhangsan'
  },
  getters: {
    fullname(state) {
      return state.name + '111'
    },
    fullname2(state,getters,rootState) {
      return getters.fullname + rootState.name
    }
  },
  mutations: {},
  actions: {}
}

const moduleB = {
  state: {},
  getters: {},
  mutations: {},
  actions: {}
}

const stote = new Vuex.Stote({
  state:{
    name: 'lisi'
  },
  //默认会把 a,b 对象放入 state 中
  modules: {
    a: moduleA,
    b: moduleB
  }
})

stote.state.a // moduleA 的状态
stote.state.b // moduleB 的状态
// 访问模块中的 state
<p>{{$store.state.a.name}}</p> // zhangsan

// 访问模块中的 getters ,直接访问,先去内部找,没有再去模块中找
<p>{{$store.getters.fullname}}</p> // zhangsan111

// 模块中的 getters 第三个参数指向实例中的 state
<p>{{$store.getters.fullname2}}</p> // zhangsan111lisi

// actions 和 mutations 用法也是直接用
3.3.6 调用其他模块的方法和属性
打印 action 的第一个参数

commit 用于调用 mutation,当前模块和其他模块
dispatch 用于调用 action,当前模块和其他模块
getters 用于获取当前模块 getter
state 用于获取当前模块 state
rootState 用于获取其它模块 state
rootGetters 用于获取其他模块 getter
在一个模块的actions中调用其他模块的actions

dispatch('vip/get', {}, {root: true}) 
参数一:是其他模块的 actions 路径,。
参数二:是传给 actions 的数据, 如果不需要传数据, 也必须预留,
参数三:是配置选项, 申明这个 acitons 不是当前模块的
在一个模块需要使用其他模块的getters

rootGetters['vip/get']
3.4 事件总线

组件发射一个事件this.$bus.$emit('aaa',参数),其他组件都可以通过this.$bus.$on('aaa',callback)监听,通过this.$bus.$off('aaa',callback)取消监事件函数(一定要传函数,不然会全局取消事件)

  • 需要在 main.js 里添加一个原型属性$bus

    // main.js
    Vue.prototype.$bus = new Vue()
    
    // 发射事件
    this.$bus.$emit('imgLoad')
    
    // 注册监听事件
    this.$bus.$on('imgLoad',this.itemImgListener)
    
     // 移除监听事件
    this.$bus.$off('imgLoad',this.itemImgListener)
    

4、环境变量配置

  1. 设置开发环境的环境变量

    • 在根目录创建一个.env.development的文件

    • 设置开发环境变量 (注意点:以VUE_APP_变量名开头)

      VUE_APP_BASEURL = http://127.0.0.1/heimamm/public
      
    • 访问:process.env.VUE_APP_变量名

      alert(process.env.VUE_APP_BASEURL)
      
  2. 设置生产环境的环境变量

    • 在根目录创建一个.env.production的文件

    • 设置开发环境变量 (注意点:以VUE_APP_变量名开头)

      VUE_APP_BASEURL = http://autumnfish.cn/heimamm/public
      
    • 访问:process.env.VUE_APP_变量名

      alert(process.env.VUE_APP_BASEURL)
      

    注意:设置完成后,需要重新启动脚手架

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值