失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > vue项目实战(移动端)

vue项目实战(移动端)

时间:2021-10-23 21:09:22

相关推荐

vue项目实战(移动端)

相关资料#

vue-cli脚手架:vue2脚手架vue3脚手架:vitevue官网: [介绍 — Vue.jsvscode插件 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.jsmodule.exports = {lintOnSave: false}

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

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

(三) devtool#

vue开发调试工具

下载添加到chrome扩展程序里

(四) 添加less支持#

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

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

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

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

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

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

npm i vue-router安装路由插件在src创建views文件夹, 创建各个模块的组件在src内创建router文件夹, 新建index.js(代码如下)在main.js里, 把router挂载到vue的实例配置路由出口, 详见下方第(2)点router-view使用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 = falsenew Vue({// 把router挂载到vue实例router,render: h => h(App),}).$mount('#app')

(2) router-view#

路由出口路由匹配到的组件将渲染在这里在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组件中的属性,用来做选中样式的切换;

只要路由中包含to里面的路由, 就能匹配到, 就会高亮, 比如: /product, /product/list, /product/detail都会使下面的第二个router-link高亮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配置方式

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

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

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

const router = new VueRouter({mode: 'history', // 默认hashroutes: [...]})

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')}

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

知识点(背诵):

父传子: 父组件通过(绑定)属性的方式传数据给子组件, 子组件使用props接收数据子传父: 父组件在子组件上绑定一个自定义事件, 子组件通过$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. 子传父#

父组件在子组件上绑定一个自定义事件(事件名称我们自己定义的, vue本身是没有这个事件的)父组件给自定义事件绑定一个函数, 这个函数可以接受来自子组件的数据子组件使用$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拦截器(背诵)#

对ajax请求进行拦截 在请求头添加token对ajax响应数据进行拦截 统一处理请求失败的情况, 这样就不需要在每个组件里处理失败的情况有些接口需要登录才能访问, 在没登录的情况下跳转到登录页面

import axios from "axios";import Vue from "vue";import { Toast } from "vant";Vue.use(Toast);const service = axios.create({baseURL: ":3003",timeout: 50000, // 请求超时时间(因为需要调试后台,所以设置得比较大)});// request 对请求进行拦截service.interceptors.request.use((config) => {// 开启loadingToast.loading({message: "加载中...",forbidClick: true,loadingType: "spinner",});// 请求头添加tokenconfig.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组件之前执行以下代码ponent('button-counter', {data: function () {return {count: 0}},template: '<button v-on:click="count++">你打了我 {{ count }} 次</button>'})// 在其他组件就可以使用<template><div><button-counter></button-counter></div> </template>

// 改造checkbox, 官网例子ponent('base-checkbox', {model: {prop: 'checked',event: 'change'},props: {checked: Boolean},template: `<inputtype="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为例

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

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

知识点:

通过params传参, 使用$route.params接收参数动态路由传参, 使用$route.params接收参数通过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;

(十四) 模拟数据#

文档地址: json-server - npmnpm i json-server -g //全局安装根目录创建db.json启动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" }}

访问接口

http://localhost:3000/posts/1

将命令添加到package.json, 可以使用 npm run json 启动项目

"scripts": {"json": "json-server --watch db.json" },

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

vue中computed和watch区别 - 简书

computed的作用watch的作用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创建的对象#

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

(2) 相关概念#

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

// getters派生状态

// 1. 在 src/store/index.jsstate: {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 修改状态(异步)

定义状态定义mutation, 通过mutation来修改状态定义action , 通过action来提交(commit)mutation用户派发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.定义mutaionmutations: {// 修改tokenset_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的mutationset_age(state, payload) {state.age = payload;},},// 3.定义actionactions: {LOGOUT(store,payload) {$http.post('/user/logout').then(res=> {// 清除token和mit('set_token','');mit('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.jsexport default {state: {cartNum: 100 },getters: { },mutaions: { },actions: {}}// src/store/modules/type.jsexport 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应用#

创建仓库 需要先安装vuexnpm i vuex --save创建仓库挂载仓库

// 1. src/store/index.jsimport Vue from 'vue';import Vuex from 'vuex';Vue.use(Vuex);// 创建仓库const store = new Vuex.Store({});export default store;// 2. 挂载到根实例 /src/main.jsimport 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,},});

获取状态直接获取this.$store.state.username

<template><div><p>username: {{$store.state.username}}</p></div></template> <script> export default {created() {console.log(this.$store.state);} };</script>

通过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.定义mutationimport Vue from "vue";import Vuex from "vuex";Vue.use(Vuex);// 创建仓库const store = new Vuex.Store({// 定义状态state: {username: "张三",age: 100,},// 定义mutaionmutations: {/*** 定义修改username的muation * @param {*} state 状态* @param {*} payload 传入的新数据*/set_username(state,payload) {state.username = payload;},// 定义修改age的mutationset_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.$mit('set_username','李四'); }}};</script>

项目应用

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

vuex持久化

安装插件npm i vuex-persistedstate -S应用插件

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) 对比

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

(2) 操作

cookie使用相关js库_js_-_cookie_sessionStorage,localStorage使用其自带方法

// 存储数据localStorage.setItem(key,value); // 比如:localStorage.setItem('username','张三')// 获取数据localStorage.getItem(key); // 比如: localStorage.getItem('username');// 清除数据localStorage.clear();

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

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

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

项目应用

在vuex里定义token状态和相关的mutation在登录成功的时候, 把token存入vuex在axios的拦截器里, 把token放入请求头, 这样, 每次发请求的时候, 都会自动带上token

// 1. 在vuex里定义token状态和相关的mutationstate: {token: "",username: "张三",age: 100,phone: "123456789",},// 定义mutaionmutations: {// 修改tokenset_token(state,payload) {state.token = payload},}// 2. 在登录成功的时候, 把token存入vuex$http.post('/user/login',data).then(res=> {// 把手机号码存入store, 修改phone状态this.$mit('set_phone',this.phone);// 把token存入storethis.$mit('set_token',res.result.token);// 从哪里来回哪里去this.$router.go(-1); })// 3. 在axios的拦截器里, 把token放入请求头, 这样, 每次发请求的时候, 都会自动带上tokenimport axios from "axios";import Vue from "vue";import { Toast } from "vant";// 导入storeimport store from '@/store/index';Vue.use(Toast);// request 对请求进行拦截service.interceptors.request.use((config) => {// 获取tokenlet token = store.state.token; // 开启loadingToast.loading({message: "加载中...",forbidClick: true,loadingType: "spinner",});// 请求头添加tokenconfig.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) 微信支付流程#

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

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

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

轮询, 定时给服务器请求, 询问结果, 直到有结果为止, 轮询不需要服务器特别的支持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://:3003/socket");// onopen连接结果ws.onopen = () => {console.log("连接成功");};// 等待后端推送信息ws.onmessage = (res) => { this.result = res.data;};},},};</script>

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

解决办法

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

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

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

被keep-live包裹的组件会被缓存使用keep-alive的组件crated和mounted只会执行一次离开组件会触发deactivated生命周期(只有被缓存的组件才有的生命周期)进入组件会触发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 = ":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适配原理#

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

(4) vue项目配置rem#

安装插npm i amfe-flexible --save在main.js导入插件import 'amfe-flexible'px自动转rem 安装插件npm i postcss-pxtorem@5.1.1在/vue.config.js添加px2rem插件,把项目中的px转为rem

const pxtorem = require("postcss-pxtorem");module.exports = {css: {loaderOptions: {// 后处理器配置postcss: {plugins: [// 把px转为rempxtorem({rootValue: 37.5,propList: ["*"]})]}}}};

插件会修改html和body的字体大小, 而字体会继承, 所以要重新设置body的font-size为合适的字体大小

(二十五) mixin(混入)#

mixin 其实是一个对象,里面的结构大致跟普通组件的 script 里面的一样,有 data 属性,钩子函数和方法等 混入 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。

1.组件内混入#

(1) 混入对象的生命周期先执行 (2) data里的状态若有重名, 取的是组件里的状态

// mixin.jsexport default {data: function() {return {username: "huruqing",age: 100};},created() {console.log('这是混入对象')},methods: {say() {console.log('hahahhahahha');}}};// demo.vue<template><div><p>{{username}}</p><p>{{msg}}</p><p>{{age}}</p></div></template><script>import mixin from './mixin'export default {mixins:[mixin],data() {return {username: '张三',msg: 'hahahahahahha'}},created() {console.log('组件的created');this.say();}}</script>

2.全局混入#

// mixin.jsexport default {methods: {loadingStart() {this.$toast.loading({message: '加载中...',forbidClick: true,duration:0});},loadingFinish() {this.$toast.clear();}}}// main.js,这个代码放在Vue.use(Vant)之后import mixin from '@/utils/mixin';Vue.mixin(mixin);// 其他组件就可以直接使用下面代码来显示loadingthis.loadingStart();

(二十六) watch监听对象#

普通的监听方法, 对对象无效watch的参数 handler 监听器(有改变就执行immediate 马上执行deep 监听引用数据类型

<template><div><input type="text" v-model="obj.username" /><p>{{ obj.username }}</p></div></template><script>export default {data: function () {return {obj: {username: "张三",},};},watch: { obj: {// 发生改变时执行的函数handler(newObj) {console.log(newObj.username);},// 首次绑定watch就执行immediate: true,// 深层监控,不设置,引用数据类型监控不到deep: true}},};</script>

(二十七) props检查类型#

// demo.vue<template><div><!-- 不传参数 --><Son/> <!-- 传了一个字符串 --><!-- <Son :msg="'他是张三'" /> --><!-- 传了一串数字 --><!-- <Son :msg="22222"/> --></div></template><script>import Son from "./Son.vue";export default {components: {Son,} };</script> // Son.vue<template><div>{{msg}}</div></template><script>export default {// props: ['msg']props:{msg: {type:String,default: 'hello'}}}</script>

(二十八) ref获取dom节点和子组件实例#

ref可以获取原生dom节点和子组件实例

<template><div><span ref="demo">ref例子</span> <button @click="handleClick">点击</button><hr /><Son ref="son" /></div></template><script>import Son from "./Son.vue";export default {components: {Son,},methods: {handleClick() {console.log(this.$refs.demo.innerText);console.log(this.$refs.son.msg);},},};</script> // Son.vue<template><div>{{msg}}</div> </template><script>export default { data() {return {msg: 'hello',title: '2222222'}}}</script>

应用

// 父组件控制子组件的显示和隐藏(子组件无状态)// demo.vue<template><div><button @click="show = !show">点击</button><hr><Son :show="show" /></div></template><script>import Son from "./Son.vue";export default {components: {Son,},data() {return {show: true,};},};</script> // Son.vue<template><div v-if="show"><p>子组件内容</p><p>子组件内容</p><p>子组件内容</p><p>子组件内容</p></div></template><script>export default {props: ['show'] }</script>

// 父组件控制子组件的显示和隐藏(子组件有状态)<template><div> <button @click="$refs.aaa.show=!$refs.aaa.show">显示弹窗</button> <Alert ref="aaa"/></div></template><script> import Alert from './Alert.vue'export default { components: {Alert}, };</script>// Son.vue(假设子组件是别人设计的组件, 多处地方在使用, 所以并不适宜去改动, 不然容易改出bug, 子组件有个show的状态来控制其显示和隐藏<template><div v-if="show"><p>{{msg}}</p><p>{{msg}}</p><p>{{msg}}</p></div></template><script>export default { data() {return {show: true,msg: 'hello vue'}}}</script>

(二十九) nextStick#

修改了数据之后, dom节点并不会立马更新, dom节点的更新是异步的, 想要拿到更新后的dom需要使用nextStick

<template><div><li ref="aa">{{ count1 }}</li><li ref="bb">{{ count2 }}</li><li ref="cc">{{ count3 }}</li><button @click="handleClick">修改数据</button></div></template><script>export default {data() {return {count1: 0,count2: 0,count3: 0,};},methods: {handleClick() {this.count1 = 1;// this.count1=1;执行后dom并不会立即更新,dom节点的更新是异步的console.log(this.$refs.aa.innerHTML); // 0// 当dom节点更新完毕, 会立即调用nextStick里的回调函数this.$nextTick(() => {console.log(this.$refs.aa.innerHTML);});this.count2 = 2;this.count3 = 3;},},};</script>

(三十) 配置跨域和模拟数据#

// 1. 在根目录新建mock文件夹// 2. 添加/category/all.json和 /product/getBanners.json, json的数据就根据接口文档进行模拟// 3. vue.config.js里配置devServer: {// 代理proxy: {// 只要请求地址有'api'都会匹配上"/api": {target: "http://81.71.65.4:3003",ws: true,// 允许跨域changeOrigin: true,pathRewrite: {"^/api": "", //通过pathRewrite重写地址,将前缀/api转为/},},},before(app) { // 模拟接口数据, 前面都加上了api是为了跨域设置的需要 // 分类列表app.get("/api/category/all", (req, res) => { res.json(require('./mock/category/all.json'));});// banner列表app.get("/api/product/getBanners", (req, res) => { res.json(require('./mock/product/getBanners.json'));});},},

如果觉得《vue项目实战(移动端)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。