vue项目实战-vue2(移动端)(代码片段)

超级小丑_Supclown 超级小丑_Supclown     2023-03-28     633

关键词:

Vue项目实战(移动端)#

相关资料#

  1. vue-cli脚手架: vue2脚手架
  2. vue3脚手架: vite
  3. vue官网: [介绍 — Vue.js
  4. vscode插件
    • vetur 必备工具
    • vue-helper 一些辅助功能
    • Vue VSCode Snippets 片段

(一) 创建项目#

01 安装vue-cli脚手架#

npm install -g @vue/cli

02 查看vue脚手架版本#

出现版本号表示成功

vue --version

03 创建一个新项目#

创建项目

vue create hello-world  // 1.创建项目

运行项目

cd hello-world  // 2.进入项目文件夹
npm run serve		// 3.运行项目

(二) 禁用Eslint#

// 根目录新增vue.config.js
module.exports = 
    lintOnSave: false

如果vue组件提示红色错误,如下图 

 解决办法: 文件 -> 首选项 -> 设置 然后输入eslint -> 选择Vetur -> 把√取消即可 

(三) devtool#

vue开发调试工具

  1. 下载 http://soft.huruqing.cn
  2. 添加到chrome扩展程序里

(四) 添加less支持#

  1. npm install less less-loader@6.0.0 --save-dev

  2. 在vue文件这样写即可, scoped表示样式只在当前文件有效, 不会影响其他组件

    ps: less-loader要安装6.0版本, 不然有兼容问题

    <style lang="less" scoped> 
    .box 
      .text 
        color: red;
      
     
    </style>
    

(五) vue路由配置(背诵)#

(1)一个简单路由配置#

  1. npm i vue-router  安装路由插件
  2. 在src创建views文件夹, 创建各个模块的组件
  3. 在src内创建router文件夹, 新建index.js(代码如下)
  4. 在main.js里, 把router挂载到vue的实例
  5. 配置路由出口, 详见下方第(2)点router-view
  6. 使用router-link进行跳转, 详见下方第(3)点路由跳转
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router); 
// 路由数组
const routes = [
    
        path: '/product',
        component: ()=>import('@/views/product/index.vue')
    ,
    
        path: '/cart',
        component: ()=>import('@/views/cart/index.vue')
    ,
]

const router = new  Router(
    routes
)
export default router;
// main.js 代码
import Vue from 'vue'
import App from './App.vue'
import router from './router/index'

Vue.config.productionTip = false

new Vue(
  // 把router挂载到vue实例
  router,
  render: h => h(App),
).$mount('#app')

(2) router-view#

  1. 路由出口
  2. 路由匹配到的组件将渲染在这里
  3. 在app.vue配置
<template>
  <div id="app"> 
    <!-- 路由出口 -->
    <router-view></router-view>
  </div>
</template>

<script>
export default 
  name: "App",
  components: ,
;
</script>

(3) 路由跳转#

// 方式一
<router-link to="/cart">cart</router-link>

// 方式二
this.$router.push('/cart');

(4) 子路由配置#

使用子路由进行模块路由配置,结构比较分明 比如我们的网站有商品模块,有列表页面和详情页面, 路由如下 /product   商品模块总路由 /prodcut/list   子路由 /product/detail   子路由


    path: '/product',
    component: () => import('@/views/product/index'),
    children: [
        
            path: 'list',
            component: ()=>import('@/views/product/children/list')
        ,
        
            path: 'detail',
            component: ()=>import('@/views/product/children/detail')
        
    ]

(5) active-class#

active-class是vue-router模块的router-link组件中的属性,用来做选中样式的切换;

  1. 只要路由中包含to里面的路由, 就能匹配到, 就会高亮, 比如: /product, /product/list, /product/detail都会使下面的第二个router-link高亮
  2. exact 表示精确匹配, 只有路由完全一样才能被匹配
<router-link to="/" active-class="on" exact>首页</router-link>
<router-link to="/product" active-class="on">product</router-link>
<router-link to="/cart" active-class="on">cart</router-link>
<router-link to="/my" active-class="on">my</router-link>
<router-link to="/order" active-class="on">order</router-link>

(6) history模式#

vue2配置方式

  1. vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。

  2. 如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面

  3. 使用history需要后端支持, vue-cli创建的devServer可以支持

    const router = new VueRouter(
      mode: 'history',  // 默认hash
      routes: [...]
    )
    

vue3配置方式

const router = createRouter( 
 	history: createWebHistory(),  // history模式
 	//history: createWebHashHistory(), // hash模式
  	routes
);     

(7) redirect重定向#

当访问 '/', 我们使用redirect使它默认跳到 '/product'


    path: '/',
    redirect: '/product'
,

(8) 404配置#

假如用户访问了一个没有的路由, 我们让它跳转到404页面

  
    path: '*',
    component:()=>import('@/components/NotFound')
  

(六) 父子组件通信(背诵)#

知识点(背诵):

  1. 父传子: 父组件通过(绑定)属性的方式传数据给子组件, 子组件使用props接收数据
  2. 子传父: 父组件在子组件上绑定一个自定义事件, 子组件通过$emit触发该自定义事件, 同时可以传入数据

1.父传子#

  • 父组件给子组件绑定属性, 属性的值是需要传递的信息
  • 子组件通过props接收父组件的信息
 // 例子1: 使用普通属性
// demo.vue
<template>
  <div>
    <h3>父组件</h3>
    <hr />
    <Son msg="hello world" username="张三"/>
  </div>
</template>

<script>
import Son from "./Son";
export default 
  components: 
    Son,
  ,
;
</script> 

// Son.vue
<template>
  <div>
    <h4>子组件</h4>
    <p>msg:  msg </p>
    <p>username:  username </p>
  </div>
</template>

<script>
export default 
  props: ["msg", "username"],
;
</script> 

// 例子2: 使用绑定属性(可传变量)
// demo.vue
<template>
  <div>
    <h3>父组件</h3>
    <hr />
    <Son :msg="msg" :username="username" />
  </div>
</template>

<script>
import Son from "./Son";
export default 
  components: 
    Son,
  ,
  data() 
    return 
        msg: '哈哈哈',
        username: '李四'
    ;
  ,
;
</script> 

// Son.vue
<template>
  <div>
    <h4>子组件</h4>
    <p>msg:  msg </p>
    <p>username:  username </p>
  </div>
</template>

<script>
export default 
  props: ["msg", "username"],
;
</script> 

父传子实践: 把首页拆分为多个组件 技巧: 如果某个部分只是做展示用, 尽量把它变成子组件

2. 子传父#

  1. 父组件在子组件上绑定一个自定义事件(事件名称我们自己定义的, vue本身是没有这个事件的)
  2. 父组件给自定义事件绑定一个函数, 这个函数可以接受来自子组件的数据
  3. 子组件使用$emit触发(调用)该事件, 并把数据以参数形式传给父组件
// 例子1: 一个简单的例子
// demo.vue
<template>
  <div>
    <h3>父组件</h3>
    <hr />
    <Son @aaa="say"/>
  </div>
</template>

<script>
import Son from "./Son";
export default 
  components: 
    Son,
  ,
  data() 
    return  
    ;
  ,
  methods: 
    say(data) 
      alert(data)
    
  

;
</script> 

// 子组件
<template>
  <div>
    <h4>子组件</h4>
    <button @click="$emit('aaa','我是子组件')">点击</button>
  </div>
</template>

<script>
export default 
  props: ["msg", "username"],
;
</script>  

(七) axios拦截器(背诵)#

  1. 对ajax请求进行拦截
    1. 在请求头添加token
  2. 对ajax响应数据进行拦截
    1. 统一处理请求失败的情况, 这样就不需要在每个组件里处理失败的情况
    2. 有些接口需要登录才能访问, 在没登录的情况下跳转到登录页面
import axios from "axios";
import Vue from "vue";
import  Toast  from "vant";
Vue.use(Toast);

const service = axios.create(
  baseURL: "http://huruqing.cn:3003",
  timeout: 50000, // 请求超时时间(因为需要调试后台,所以设置得比较大)
);

// request 对请求进行拦截
service.interceptors.request.use(
  (config) => 
    // 开启loading
    Toast.loading(
      message: "加载中...",
      forbidClick: true,
      loadingType: "spinner",
    );
    // 请求头添加token
    config.headers["token"] =
      "gg12j3h4ghj2g134kj1g234gh12jh34k12h34g12kjh34kh1g";
    return config;
  ,
  (error) => 
    Promise.reject(error);
  
);

// response 响应拦截器
service.interceptors.response.use(
  (response) => 
    Toast.clear();
    const res = response.data;
    if (res.code == 666) 
      return res;
     else 
      // 成功连接到后台, 但是没有返回正确的数据
      Toast.fail(res.msg);
    
  ,
  (error) => 
    Toast.clear();
    // 跟后台连接失败
    Toast.fail("网络异常,请稍后再试");
  
);

export default service;

(八) Sticky 粘性布局#

(九) 图片懒加载#

二、vue2.x进阶教程 | 清流

(十) 全局注册组件#

// 注册全局组件除了多了个template之外,其它跟平时写组件类似
// 在main.js,实例化vue组件之前执行以下代码
Vue.component('button-counter', 
  data: function () 
    return 
      count: 0
    
  ,
  template: '<button v-on:click="count++">你打了我  count  次</button>'
)

// 在其他组件就可以使用
<template>
	<div>
  	<button-counter></button-counter>
  </div>  
</template>
// 改造checkbox, 官网例子
Vue.component('base-checkbox', 
  model: 
    prop: 'checked',
    event: 'change'
  ,
  props: 
    checked: Boolean
  ,
  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `
)

// 然后就可以像下面这样来使用
<template>
  <div> 
   <base-checkbox v-model="flag"></base-checkbox>
		<p>flag</p>
  </div>
</template>

<script>
export default 
  data: function () 
    return 
      flag: false
    ;
  ,

</script>
// 另外需要在根目录的vue.config.js中开启运行时编译
module.exports = 
    runtimeCompiler: true

(十一) slot插槽#

元素作为承载分发内容的出口 一个内存插槽, 当内存插上之后,插槽就可以接收来自内存的信息, slot取名插槽含义也贴切, 在子组件配置插槽slot, 当父组件"插"信息进来的时候, 插槽slot就能接收到这个信息. slot插槽大大的扩展子组件的功能。 

1. vant有赞ui库中slot的例子#

<van-nav-bar title="标题" left-text="返回" left-arrow> 
   <p slot="right">
     <van-icon name="search" size="18" />
   </p>
</van-nav-bar>

2. 普通插槽#

// 父组件demo.vue代码
<template>
  <div>
    <h3>父组件</h3>
    <hr>
    <Son><button>按钮</button></Son>
  </div>
</template>

<script>
import Son from "./Son";
export default 
  components: 
    Son,
  
;
</script> 

// 子组件Son.vue
<template>
  <div>
    <slot></slot>
  </div>
</template> 

3. 具名插槽#

// father.vue代码
<template>
  <div>
    <h3>这是父组件</h3>

    <Child>
      <header slot="header" style="background: yellow">这是头部</header>
      <footer slot="footer" style="background: green;">这是底部</footer>

      <div style="border:1px solid;">
        <button>a</button>
        <button>b</button>
        <button>c</button>
        <button>d</button>
      </div>
    </Child>
  </div>
</template>

<script>
import Child from "@/components/Child";
export default 
  components: 
    Child
  
;
</script>

接收父组件带 slot="footer" 的内容
接收不带slot="xxx" 的内容

// Child.vue代码
<template>
  <div style="margin-top: 30px;background: gray;height: 200px;">
    <h5>这是子组件</h5>
		<!--接收父组件带 slot="header" 的内容-->
    <slot name="header"></slot>
		<!--接收父组件带 slot="footer" 的内容-->
    <slot name="footer"></slot>
		<!--接收剩余内容-->
    <slot></slot>
  </div>
</template>

自定义组件

// demo.vue
<template>
  <div> 
    <NavBar title="首页" @click-left="clickLeft" @click-right="clickRight"></NavBar>
  </div>
</template>

<script> 
import NavBar from './Nav-Bar.vue'
export default 
    components: 
      NavBar
    ,

    methods:
      clickLeft() 
        alert('左边被点击了'); 
      ,
      clickRight() 
        alert('右边被点击了')
      
    

</script>


// Nav-Bar.vue
<template>
    <div class="nav flex jc-sb pl-15 pr-15 bg-fff aic">
      <p class="blue flec aic" @click="$emit('click-left')">
        <van-icon name="arrow-left" />
        <span>返回</span>
      </p>
      <p>title?title:'标题'</p>
      <slot name="right"> <span  class="blue" @click="$emit('click-right')">按钮</span></slot>
    </div>
</template>

<script>
export default 
  props: ['title']

</script> 

<style lang="less">
.nav 
  height: 50px;
  .blue 
    color: #1989fa;
  


</style>

(十二) 使用ui库需要关注的三点#

以vant 的导航栏组件van-nav-bar为例

  1. 属性, 该组件提供了哪些绑定属性
  2. 事件, 该组件提供了哪些事件
  3. 插槽, 该组件提供了哪些插槽

(十三) 三种路由传参方式(背诵)#

知识点:

  1. 通过params传参, 使用$route.params接收参数
  2. 动态路由传参, 使用$route.params接收参数
  3. 通过query传参, $route.query接收参数

注意: router和route不是一回事 ​

1.通过name+params传参#

// 1.配置路由的时候添加name
  
        path: "detail",
        name: 'product-detail',
        component: () => import("@/views/order/children/detail"),
  ,


// 2.跳转
 this.$router.push(
        // 要跳转到的路由名称
        name: 'product-detail',
         params:  productId: '123' 
      )

// 3.接收参数
this.$route.params.productId

2.动态路由传参#

// 1.配置路由

  path: "detail/:productId", 
  component: () => import("@/views/product/children/detail.vue"),
,
  
// 2. 跳转
this.$router.push('/product/detai/22222')
<router-link to="/product/detail/333333">传参</router-link>

  
// 3.接收参数
 created() 
    let params = this.$route.params;
    console.log('params',params); 
  ,
  

3.通过path+query传参#

// 带查询参数,query传参会把参数拼接到地址栏,变成 /register?plan=aaa, 使用了path,参数不能通过params传递
this.$router.push( path: '/register', query:  plan: 'aaa' )
// 获取参数
this.$route.query;

(十四) 模拟数据#

  1. 文档地址:  json-server - npm
  2. npm i json-server -g    //全局安装
  3. 根目录创建db.json
  4. 启动json-server
json-server --watch db.json
// db.json

  "posts": [
     "id": 1, "title": "json-server", "author": "typicode" 
  ],
  "comments": [
     "id": 1, "body": "some comment", "postId": 1 
  ],
  "profile":  "name": "typicode" 

  1. 访问接口
http://localhost:3000/posts/1
  1. 将命令添加到package.json, 可以使用 npm run json 启动项目
 "scripts": 
    "json": "json-server --watch db.json" 
  ,

(十五) 计算属性computed和属性观察watch#

vue中computed和watch区别 - 简书

  1. computed的作用
  2. watch的作用
  3. computed和watch的区别
// computed
<template>
  <div>
    <p>姓:  xing </p>
    <p>名:  ming </p>

    <p>姓名:  xingming </p>

    <button @click="change">修改xing</button>
  </div>
</template>  

<script>
export default 
  data() 
    return 
      xing: "张",
      ming: "无忌",
    ;
  ,

  // 计算属性
  computed: 
    // xingming这个属性是由xing属性和ming计算得来
    xingming() 
      return this.xing + this.ming;
    ,
  ,

  methods: 
    change() 
      this.xing = "李";
    ,
  ,
;
</script>

(十六) vuex(背诵)#

(1) 普通对象 VS vuex创建的对象#

  1. 普通对象
    1. 创建对象
    2. 定义对象的属性
    3. 修改对象的属性
    4. 读取对象属性
  2. vuex
    1. 创建仓库
    2. 定义状态
    3. 修改状态
    4. 读取状态

(2) 相关概念#

  1. 概念vuex是什么: 创建一个仓库, 然后在仓库里定义若干状态, 并且管理这些状态. Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
  2. vuex有哪几个核心概念, 都是用来做什么的
    1. state 定义状态
    2. getters 派生状态
    3. mutation 修改状态(同步)
    4. action 修改状态(异步)
    5. module 模块化
  3. 如何使用vuex进行跨组件通信
  4. vuex持久化

// getters派生状态


// 1.  在 src/store/index.js
state: 
    token: "",
    username: "张三",
    age: 100,
    phone: "123456789",
  ,

  getters: 
    // 派生状态
    str(state) 
      return `我叫$state.username,我的年龄是$state.age`
    
  ,
    
// 2. 在组件里使用
    
<template>
	<div> 
  str
  </div>  
  
</template>    
    
import mapGetters from 'vuex';
export default 
 computed:
 		...mapGetters(['str'])
  




// action 修改状态(异步)

  1. 定义状态
  2. 定义mutation, 通过mutation来修改状态
  3. 定义action , 通过action来提交(commit)mutation
  4. 用户派发action
import Vue from "vue";
import Vuex from "vuex";
import $http from '@/utils/http';
// 导入持久化插件
import createPersistedState from "vuex-persistedstate";

Vue.use(Vuex);
// 创建仓库
const store = new Vuex.Store(
  plugins: [createPersistedState()],
  // 1.定义状态
  state: 
    token: "",
    phone: "123456789",
    username: "张三",
    age: 100,
  ,

  getters: 
    // 派生状态
    str(state) 
      return `我叫$state.username,我的年龄是$state.age`
    
  ,


  // 2.定义mutaion
  mutations: 
    // 修改token
    set_token(state,payload) 
      state.token = payload
    ,

    // 修改phone的状态
    set_phone(state, payload) 
      state.phone = payload;
    ,
    /**
     * 定义修改username的muation
     * @param * state 状态
     * @param * payload 传入的新数据
     */
    set_username(state, payload) 
      state.username = payload;
    ,

    // 定义修改age的mutation
    set_age(state, payload) 
      state.age = payload;
    ,
  ,

  // 3.定义action
  actions: 
    LOGOUT(store,payload) 
      $http.post('/user/logout').then(res=> 
          // 清除token和phone
          store.commit('set_token','');
          store.commit('set_phone','');
      )
    
   
);

export default store;


// 4.退出登录时派发action
 <p class="red" @click="logout2">退出登录</p>

methods: 
	 logout2() 
      this.$store.dispatch('LOGOUT');
       this.$router.push('/my');
    ,

// 模块化

// 1.定义模块的state getters mutaions actions
// src/store/modules/cart.js
export default 
    state: 
       cartNum: 100 
    ,

    getters:  ,
    mutaions:  ,
    actions: 

// src/store/modules/type.js
export default 
    state: 
       aaa: 333 
    ,

    getters:  ,
    mutaions:  ,
    actions: 



// 2.合并模块
import cart from './modules/cart';

const store = new Vuex.Store(
  modules:
    cart,
    type, 
  ,

                             
// 3.使用(在任何一个组件内)
<template>
	<div>
  	   $store.state.cart.cartNum 
      $store.state.type.aaa                            
  </div>                             
</template>                             
                             


(3) vuex应用#

  • 创建仓库
    1. 需要先安装vuex npm i vuex --save
    2. 创建仓库
    3. 挂载仓库
// 1. src/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);

// 创建仓库
const store = new Vuex.Store(

);

export default store;


// 2. 挂载到根实例 /src/main.js
import router from "./router/index";
import store from './store/index';

Vue.use(Vant);  

Vue.config.productionTip = false;
new Vue(
  store,
  router,
  render: (h) => h(App),
).$mount("#app");
  • 定义状态
const store = new Vuex.Store(
  // 定义状态
  state: 
    username: "张三",
    age: 100,
  ,
);
  • 获取状态
    1. 直接获取 this.$store.state.username
<template>
  <div>
    <p>username: $store.state.username</p>
  </div>
</template>   
<script> 
export default 
    created() 
      console.log(this.$store.state);
     
;
</script>
  1. 通过mapState获取, mapState是vuex提供的方法, 可以让我们更方便的获取属性
<template>
  <div>
    <p>username: username</p>
    <p>age: age</p>
  </div>
</template>  

<script>
import mapState from 'vuex';
export default  
    computed: 
      ...mapState(['username','age'])
    
;
</script>
  • 修改状态: 通过mutation进行修改
    • 修改状态只能通过mutation来修改, 不可以直接修改
    • mutation只支持同步操作
  • 步骤:
    • 定义mutation
    • 提交mutation
// 1.定义mutation
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
// 创建仓库
const store = new Vuex.Store(
  // 定义状态
  state: 
    username: "张三",
    age: 100,
  ,
  // 定义mutaion
  mutations: 
      /**
       * 定义修改username的muation 
       * @param * state 状态
       * @param * payload 传入的新数据
       */
      set_username(state,payload) 
        state.username = payload;
      ,

      // 定义修改age的mutation
      set_age(state,payload) 
        state.age = payload;
      
   
);

export default store;


// 2. 提交mutaion
<template>
  <div>
    <p>username: $store.state.username</p>
    <button @click="change">修改状态</button>
  </div>
</template>   
<script> 
export default   
    methods: 
      change() 
        // 提交mutation,参数1 mutation的名称, 参数2 新的数据
        this.$store.commit('set_username','李四'); 
      
    
;
</script>

项目应用

  1. 定义一个状态 phone, 值为空
  2. 登录成功之后, 修改phone的状态
  3. 在个人中心页面, 获取phone状态
    1. 若有phone, 显示phone
    2. 若没有, 就显示立即登录

vuex持久化

  1. 安装插件 npm i vuex-persistedstate -S
  2. 应用插件
import Vue from "vue";
import Vuex from "vuex";
// 导入持久化插件
import createPersistedState from "vuex-persistedstate"; 
Vue.use(Vuex); 
an 
const store = new Vuex.Store(
  plugins: [createPersistedState()],
)

(十七) 浏览器缓存cookie,sessionStorage,localStorage#

(1) 对比

  1. 三者都是浏览器缓存,可以将数据存储在浏览器上, 其中后两者是html5的新特性
  2. cookie存储容量较小,一般浏览器4KB, 后两者5M
  3. sessionStorage:临时存储, 浏览器关闭就销毁, localStorage: 永久存储, 销毁需要手动销毁

(2) 操作

  1. cookie使用相关js库 _js_-_cookie_
  2. sessionStorage,localStorage使用其自带方法
// 存储数据
localStorage.setItem(key,value);  // 比如:localStorage.setItem('username','张三')
// 获取数据
localStorage.getItem(key);        // 比如: localStorage.getItem('username');
// 清除数据
localStorage.clear();

(十八) token(令牌)和session(会话)#

相同点: 两者都是用来识别用户的

  1. session会话, sessionId
    1. 对于特定接口, 前端需要登录才能访问, 所以第一次访问时需要登录, 登录成功, 服务器会返回一个sessionId
    2. 下次前端再访问同一个接口的时候, 把sessionId带上(cookie), 这样服务器就能识别是谁在访问, 如果这个人已经登录过, 就不再需要再登录, session一般设有效期
  2. token令牌, 或叫同行证
    1. 前端在登录成功时, 服务器会把用户的相关信息加密, 得到一个密文, 这就是token, 返回给前端
    2. 前端再次访问接口时, 把token带上, 服务器端收到token就对它进行解密, 得到用户信息

项目应用

  1. 在vuex里定义token状态和相关的mutation
  2. 在登录成功的时候, 把token存入vuex
  3. 在axios的拦截器里, 把token放入请求头, 这样, 每次发请求的时候, 都会自动带上token
// 1.  在vuex里定义token状态和相关的mutation
state: 
    token: "",
    username: "张三",
    age: 100,
    phone: "123456789",
  ,
  // 定义mutaion
  mutations: 
    // 修改token
    set_token(state,payload) 
      state.token = payload
    ,
  
  
  
  // 2. 在登录成功的时候, 把token存入vuex
   $http.post('/user/login',data).then(res=> 
        // 把手机号码存入store, 修改phone状态
        this.$store.commit('set_phone',this.phone);
        // 把token存入store
        this.$store.commit('set_token',res.result.token);
        // 从哪里来回哪里去
        this.$router.go(-1); 
      )

  // 3. 在axios的拦截器里, 把token放入请求头, 这样, 每次发请求的时候, 都会自动带上token
  
  
import axios from "axios";
import Vue from "vue";
import  Toast  from "vant";
// 导入store
import store from '@/store/index';
Vue.use(Toast);
  
  
// request 对请求进行拦截
service.interceptors.request.use(
  (config) => 
    // 获取token
    let token = store.state.token;  
    // 开启loading
    Toast.loading(
      message: "加载中...",
      forbidClick: true,
      loadingType: "spinner",
    );
    // 请求头添加token
    config.headers["user-token"] = token;
    return config;
  ,
  (error) => 
    Promise.reject(error);
  
);

(十九) vue过滤器#

作用: 格式化数据

// 组件内的过滤器
<template>
  <div>
      num | f
  </div>
</template>   

<script>
export default 
  data() 
    return 
      num: 10
    
  , 
  filters: 
    f(num) 
      return Number(num).toFixed(2);
    
  

</script>

// 全局过滤器
Vue.filter('fMoney', (money)=> 
  let num = money/100;
  return num.toFixed(2);
)
new Vue()

// 定义好全局过滤器后, 组件内可以直接使用
<template>
  <div>
      num | fMoney
  </div>
</template>   

<script>
export default 
  data() 
    return 
      num: 1000
    
  , 

</script>

(二十) 微信支付-轮询和websocket#

(1) 微信支付流程#

  1. 用户点击提交订单, 商户(服务器端)创建订单, 并返回订单信息和支付二维码给用户
  2. 用户扫码支付(货值调起微信支付)
  3. 支付平台收到钱后, 返回支付信息给用户, 同时通知商户(服务器端)已收到用户的钱
  4. 商户(服务器端)修改订单的状态
  5. 用户(web端)获取支付结果, 得到结果后做相应操作
    1. 轮询方式
    2. websocket

(2) 获取支付结果的两种方式#

获取支付结果, 可以使用轮询或者websocket

  1. 轮询, 定时给服务器请求, 询问结果, 直到有结果为止, 轮询不需要服务器特别的支持
  2. websocket, 前端只需跟后台建立连接即可(长连接), 有了结果服务器可以给前端主动推送信息, websocket是长连接, 而http请求是一次性连接, websocket需要服务器端创建socket接口, 很多网站的客服服务就是使用websocket做的
// 轮询
<template>
  <div class="payment pay"></div>
</template>

<script>
export default 
  data() 
    return 
      timer: null,
      orderId: 'sdfasdfasdfasdfasdfasdfas'
    ;
  ,

  created() 
    this.waitResult();
  ,

  beforeDestroy() 
    // 销毁定时器
    clearInterval(this.timer);
  ,

  methods: 
    async waitResult() 
      // 创建定时器
      this.timer = setInterval(async () => 
        let res = await this.$axios.post("/order/detail", 
          orderId: this.orderId,
        );
        if (res.result.orderStatus === "01") 
          clearInterval(this.timer);
          // 支付成功, 返回首页
          this.$router.push("/");
        
      , 2000);
    ,
  ,
;
</script> 
// webSocket
<template>
  <div>result</div>
</template>

// webSocket
<template>
  <div>result</div>
</template>

<script>
export default  
  data() 
    return 
      result: ''
    
  ,

  created() 
    this.connect();
  ,
  methods: 
    connect() 
      this.result = '等待支付结果...';
      // 跟后端建立连接
      var ws = new WebSocket("ws://huruqing.cn:3003/socket");
      // onopen连接结果
      ws.onopen = () => 
        console.log("连接成功");
      ;
      // 等待后端推送信息
      ws.onmessage = (res) =>   
        this.result = res.data;
      ;
    ,
  ,
;
</script> 

(二十一) 进入组件, 滚动条不在顶部的问题#

解决办法

// router/index.js
const routes = [...];
const router = new Router(
  mode: "history",
  scrollBehavior: () => (
    y: 0
  ),
  routes
);

(二十二) keep-alive(背诵)#

问题: 用户从列表的第3页, 点击某个商品进入了商品详情, 当用户点击返回的时候, 默认会返回到列表页的第一页而不是第3页, 这样的体验很不好, 所以我们希望可以回到列表页的原来位置, 这样的用户体验会比较好. 分析: 之所以会回到第一页, 是因为返回到列表页的时候, 组件会重新创建, 从新执行created方法, 所以页面页重新渲染 解决: 使用keep-alive可以缓存组件的状态, 具体做法: (1) 对列表页使用keep-alive, 使其即使离开了组件, 也不会销毁 组件挂载完毕的时候绑定滚动事件, 记录滚动的位置 (2) 从详情页返回的时候, 滚动的原来的位置(在activated生命周期) **注: **

  1. 被keep-live包裹的组件会被缓存
  2. 使用keep-alive的组件crated和mounted只会执行一次
  3. 离开组件会触发deactivated生命周期(只有被缓存的组件才有的生命周期)
  4. 进入组件会触发activated生命周期
// 方法1 APP.vue
<template>
  <div id="app"> 
    <keep-alive>
      <router-view></router-view>
    </keep-alive>
  </div>
</template> 

// 方法2, 给路由配置keepAlive属性
// (1) /router/index.js
 
    path: "/product",
    component: () => import("@/views/product/index.vue"),
    redirect: "/product/list",
    children: [
      
        path: "list",
        // 缓存次组件
        meta: 
          keepAlive: true,
          tittle: '列表'
        ,
        component: () => import("@/views/product/children/list2.vue"),
      ,
      
        path: "detail/:productId",
        component: () => import("@/views/product/children/detail.vue"),
      ,
    ],
  ,

// APP.vue
<template>
  <div id="app"> 
      <!-- 渲染需要缓存的组件 -->
     <keep-alive>  
        <router-view v-if="$route.meta.keepAlive"></router-view>
     </keep-alive>

      <!-- 渲染不需要缓存的组件 -->
      <router-view v-if="!$route.meta.keepAlive"></router-view>
  </div>
</template> 

// 上面需求的实现
(1) 在mounted绑定window.scroll事件, 滚动的时候保存滚动条的位置
(2) 返回时候, 重新滚动到原来保存的位置 

  mounted() 
    window.addEventListener('scroll',()=>  
      // 保存滚动条位置
      if (window.scrollY>0) 
          this.scrollHeight = window.scrollY;
      
    ,false);
  ,  

  // 进入组件
  activated()  
    // 滚动到最初的位置
    setTimeout(()=> 
      window.scrollTo(0,this.scrollHeight); 
    ,0)
  ,

(二十三) 配置环境变量#

项目开发的时候, 一般会有多个环境, 比如开发环境, 测试环境, 生产环境, 我们调用接口的时候, 不同环境调用不同的接口, 所以要配置环境, ,方便访问。

// utils/http.js 核心代码

let env = process.env.NODE_ENV;
let baseURL;
// 开发环境
if (env === "development") 
  baseURL = "http://localhost:3003";
 else 
  baseURL = "http://huruqing.cn:3003";


const service = axios.create(
  // 如果换了新的项目, 需要更换为新的接口地址
  baseURL: baseURL,
  timeout: 50000, // 请求超时时间(因为需要调试后台,所以设置得比较大)
);

(二十四) rem移动端适配#

(1) 元素单位有哪些:#

(2) rem和根标签字体大小的关系#

// rem例子 demo1.html
<!DOCTYPE html>
<html lang="en" style="font-size: 100px;">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style> 
        div
            width: 1rem;
            height: 1rem;
            background-color: gray;
        
    </style>
</head>
<body>
    <div>

    </div>
</body>
</html>

// rem例子 demo1.html
<!DOCTYPE html>
<html lang="en" style="font-size: 112px;">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style> 
        div
            width: 1rem;
            height: 1rem;
            background-color: green;
        
    </style>
</head>
<body>
    <div>

    </div>
</body>
</html>

(3) 移动端rem适配原理#

  1. 设置一个设备参考值(比如iPhone6)
  2. 跟据设备宽度等比缩放根标签字体大小

(4) vue项目配置rem#

  1. 安装插 npm i amfe-flexible --save
  2. 在main.js导入插件 import 'amfe-flexible'
  3. px自动转rem
    1. 安装插件 

      ??基于vue2+vuex+vue-router构建的移动端微应用(代码片段)

      ...于vue2、vuex、vue-router核心概念的理解与掌握。前言做这个项目的初衷其实很简单,我司之前一直用angular、react进行PC端项目的开发,但是最近新开展了一些项目打算用vue来做移动端的开发(紧跟大厂的步伐?)。无奈之前只是看了看v... 查看详情

      vue2.0+element-ui实战案例(代码片段)

      ...vue周边的库vue-cli,vue-router,axios,moment,Element-ui搭建一个前端项目案例,后端数据接口,会使用json-server快速搭建一个本地的服务,方便对数据的增删改查,利用以上技术我们会搭建一个vue案例,效果展示图:   以上就是... 查看详情

      vue2+vue-router+es6,vue2最新spa项目实战-仿懂球帝-爆炸足球

      ...通过webpack设置HTTP代理的方式请求懂球帝官网api获取数据项目截图安装与运行gitclonehttps:// 查看详情

      vue项目实战——实现一个任务清单基于vue3.x全家桶(简易版)(代码片段)

      Vue3.x项目实战(一)项目名参考链接Vue2.x_todoList基于Vue2.x实现一个任务清单Vue2.x_GitHub搜素案例基于Vue2.xGitHub搜素案例文章目录Vue3.x项目实战(一)Vue3.x实现todoList1、前言2、项目演示(一睹为快)3、涉及知... 查看详情

      基于vue2.x的小米商城移动端项目

      ...已经有一段时间,为了检验自己的学习成果,决定做一个项目作为一个阶段性总结,项目花了差不多半个月时间,目前实现了7个页面,商城的主要功能基本实现,代码已经放到github上面.    这个项目把大部分vue的... 查看详情

      vue2到vue3实战必备技能(代码片段)

      ...Vue2脚手架搭建;1.安装nodejs2.全局安装vue-cli3.创建vue项目4.项目配置5.运行项目二、Vue2基础语法和指令;1.插值表达式;2.v-text和v-html3.v-model数据双向绑定4.v-bind5.v-if和v-show6.v-for7.v-on7.1.事件修饰符:前言Vue概述:... 查看详情

      vue2.x实战之后台管理系统开发(代码片段)

      2.开发前须知我的后台管理系统项目运用了如下框架/插件:Vue2.x——项目所使用的js框架,我所使用的版本是:2.1.10vue-router2——Vue2.x配套路由,我所使用的版本是:2.3.0Element——UI框架,饿了么出品,我所使用的版本是:1.2.8Echa... 查看详情

      vue2电商实战项目(代码片段)

      ...翻译官,比如ES6语法转换成ES5语法脚手架使用-命令行创建项目:vuecreate项目名称-node_modules:放置项目依赖的地方-public:一般放置一些共用的静态资源,打包上线的时候,public文件夹里面资源原封不动打包到dist文件夹里面-src:开发者... 查看详情

      vue2,vue3移动端实现表格固定和首列固定(代码片段)

      ...了,上个月业务繁忙,事情比较多,最近在做移动端中发现了一个好玩的事情,那就是移动端中实现表格,固定列有哪些方法:1.position:sticky粘性布局,这个属性也可以实现行和列的固定,在pc端上... 查看详情

      vue2,vue3移动端实现表格固定和首列固定(代码片段)

      ...了,上个月业务繁忙,事情比较多,最近在做移动端中发现了一个好玩的事情,那就是移动端中实现表格,固定列有哪些方法:1.position:sticky粘性布局,这个属性也可以实现行和列的固定,在pc端上... 查看详情

      vue2移动端使用vee-validate进行表单验证(代码片段)

      使用vee-validate时若要使用中文版本提示时,vee-validate的版本需要注意"vee-validate":"2.0.0-rc.25"在main.js里添加如下代码importVeeValidate,Validatorfrom‘vee-validate‘importCNfrom‘vee-validate/dist/locale/zh_CN.js‘Validator.addLoca 查看详情

      vue项目实战——实现一个任务清单(学以致用,两小时带你巩固和强化vue知识点)(代码片段)

      Vue2.x项目实战(一)文章目录Vue2.x项目实战(一)Vue2.x实现todoList1、前言2、项目演示(一睹为快)3、涉及知识点4、项目详情(附源码及解析)5、写在最后的话Vue2.x实现todoList1、前言如果你对vue的... 查看详情

      vue快速入门(从入门到实战)(idea版)一篇文章即可快速入门(可直接开发前后端分离项目)(代码片段)

      Vue快速入门一、Vue快速入门1、认识Vue2、安装Node.js(1)进入Node.js官网[https://nodejs.org/zh-cn/](https://nodejs.org/zh-cn/)2、NPM二、工程案例1、创建工程2、安装Vue(1)初始化项目(2)安装Vue3、创建HTML文件࿰ 查看详情

      基于vue2+nuxt构建的高仿饿了么(2018版)(代码片段)

      ...务端渲染,适合刚接触或者准备上vuessr的同学参考和学习项目地址如遇网络不佳,请移步国内镜像加速节点效果演示查看demo请戳这里(请用chrome手机模式预览)移动端扫描下方二维码API接口文档接口文档地址(基于apidoc)技术... 查看详情

      移动端vue项目模板(代码片段)

      模板项目技术栈vue-cli3脚手架vue-routervuex跨组件通信rem适配axios拦截器tokenPromisevant-ui核心知识体系简介vue-cli3脚手架创建项目开发环境和生产环境配置vue-router路由路由配置子路由配置路由守卫,设置页面标题和根据登录状态判断是... 查看详情

      前端高级(二十五)vue2.0项目实战一配置简要说明代码简要说明import/export轮播和列表例子(代码片段)

      ...,中间件等,所在位置 2、package.jason   配置当前项目要安装的中间件和依赖文件"name":"my-app","version":"1.0.0","description":"AVue.jsproject","author":"","private":true,"sc 查看详情

      精讲前端实战项目之移动端网易云首页(附源码)(代码片段)

      ...是没有一个自己的作品,那是因为缺乏练习一些实战项目。今天这个就是一个很好的实战例子——移动端网易云首页,刚开始就做一些简单的静态网页。本人会用到很多标签,如果忘记了可以查 查看详情

      精讲前端实战项目之移动端网易云首页(附源码)(代码片段)

      ...是没有一个自己的作品,那是因为缺乏练习一些实战项目。今天这个就是一个很好的实战例子——移动端网易云首页,刚开始就做一些简单的静态网页。本人会用到很多标签,如果忘记了可以查 查看详情