跳至主要內容

Vue 项目总结

Mr.Chen开发日志项目总结Vue大约 15 分钟约 4411 字

1.搜索历史纪录

  • 使用 localStorage 解决 vuex 在页面刷新后数据被清除的问题

使用逻辑

  • 1.在 vuex 获取到用户信息时(例:state.user_id =user_info.user_id),同时把这个信息存入 localStorage
  • 2.定义一个 mutation 方法,再次把数据从 localStorage 传给 state;
  • 3.我们的问题发生在页面刷新时,可能很多同学就会去想在监听页面刷新事件时触发上面第 2 步的那个方法。其实不用这么麻烦,在 computed 属性时,只要我们判断一下用户信息是否为空,为空时调用第 2 步的那个方法就好了,然后我们把这个 state return 给那个响应属性就好了;
  • 4.至于为什么这么麻烦在 state 和 localStorage 中传来传去,是因为 state 的值刷新后会没了,而 localStorage 的值不能响应式地变化(Vue 仅可以对其管理的数据做响应式处理,可以理解为 data 中的数据,localStorage 并不在 Vue 的管理下,自然不会有响应特性);

2.v-model 指令的三个参数

1.number

3.vee-validate 表单验证

安装

npm install vee-validate --save

配置和使用

配置使用主要是 3 个文件。

  • validate.js :自己创建一个文件,单独将关于表单验证的代码抽离出来,从 node_modules 中引入 VeeValidate,配置相关项
  • main.js :vue 主文件入口,引入 validate.js
  • form.vue :表单组件
validate.js
import Vue from 'vue'
import VeeValidate, { Validator } from 'vee-validate'
import zh from 'vee-validate/dist/locale/zh_CN' //引入中文文件

// 配置中文
Validator.addLocale(zh)

const config = {
  locale: 'zh_CN'
}

Vue.use(VeeValidate, config)

// 自定义validate
const dictionary = {
  zh_CN: {
    messages: {
      email: () => '请输入正确的邮箱格式',
      required: field => '请输入' + field
    },
    attributes: {
      email: '邮箱',
      password: '密码',
      name: '账号',
      phone: '手机'
    }
  }
}

Validator.updateDictionary(dictionary)

Validator.extend('phone', {
  messages: {
    zh_CN: field => field + '必须是11位手机号码'
  },
  validate: value => {
    return value.length == 11 && /^((13|14|15|17|18)[0-9]{1}\d{8})$/.test(value)
  }
})
3-1 引入表单验证依赖文件,并且引入的是支持中文错误提示的文件。
import Vue from 'vue'
import VeeValidate, { Validator } from 'vee-validate'
import zh from 'vee-validate/dist/locale/zh_CN' //引入中文文件
3-2 进行中文错误提示的配置。
// 配置中文
Validator.addLocale(zh)

const config = {
  locale: 'zh_CN'
}

Vue.use(VeeValidate, config)
3-3 如果你想自定义表单验证的提示语,不配置有默认。
// 自定义validate
const dictionary = {
  zh_CN: {
    messages: {
      email: () => '请输入正确的邮箱格式',
      required: field => '请输入' + field
    },
    attributes: {
      email: '邮箱',
      password: '密码',
      name: '账号',
      phone: '手机'
    }
  }
}

Validator.updateDictionary(dictionary)
//message : 提示语。
//attributes: 就是 filed。
3-4 扩展自定义的验证,比如这边自定义了电话的表单验证。
Validator.extend('phone', {
  messages: {
    zh_CN: field => field + '必须是11位手机号码'
  },
  validate: value => {
    return value.length == 11 && /^((13|14|15|17|18)[0-9]{1}\d{8})$/.test(value)
  }
})

main.js 中代码

import './static/js/validate.js'

只需要将 validate.js 引入主文件入口就完成了铺路工作,现在就可以开始写表单界面了。

form.vue 组件中的代码:

<div class="layui-form-item">
    <label class="layui-form-label">账户</label>
    <div class="layui-block">
    <input v-model="name" v-validate="'required|min:3|alpha'" :class="{'input': true, 'is-danger': errors.has('name') }" type="text" name="name" class="layui-input" placeholder="账户">
    <span v-show="errors.has('name')" class="text-style" v-cloak> {{ errors.first('name') }} </span>
    </div>
</div>

这是其中的一个 input 拿出来讲:

1、首先在 input 中你得有 name 属性。

2、v-validate 属性:管道形式进行过滤,验证条件。

3、span 就是错误提示 。


errors.first('field') // 获取关于当前field的第一个错误信息
collect('field')  // 获取关于当前field的所有错误信息(list)
has('field') // 当前filed是否有错误(true/false)
all()  // 当前表单所有错误(list)
any()  // 当前表单是否有任何错误(true/false)12345

到这边你可以完成基础的表单验证了,不同的项目都会有不同的需求,表单验证也不一样,但是 VeeValidate 支持你去扩展,完成各种不同的需求。你可以参照着官网文档,造一个自己的表单验证,这样不同项目时只需要进行简单的修改就能用上了。

官网:https://baianat.github.io/vee-validate/configuration.html

4.vue-amap

  • 基于 Vue 2.x 与高德的地图组件

4.1 到高德地图注册一个帐号,创建应用,创建一个 key

4.2 安装

npm install vue-amap --save

配置
// 引入vue-amap
import AMap from 'vue-amap'
Vue.use(AMap)

// 初始化vue-amap
AMap.initAMapApiLoader({
  // 申请的高德key
  key: 'YOUR_KEY',
  // 插件集合
  plugin: ['AMap.PlaceSearch', 'AMap.Geolocation']
})

4.3 开始使用

<input type="text" ref="searchText" id="searchText" @keyup="keyUpSearch" placeholder="请输入地址"/>


<div class="address_items" id="address_result" v-if="searchData.length > 0">
  <div class="address_item" v-for="item in searchData">
    <div class="title">{{ item.name }}</div>
    <div class="description">{{ item.pname }}{{ item.cityname }}{{ item.address }}</div>
  </div>
</div>

methods 里面添加对应的 keyUpSearch 方法
<script>
export default {
  methods: {
    keyUpSearch() {
      var _this = this;
      var txt = this.$refs.searchText.value;
      AMap.service(["AMap.PlaceSearch"], function() {
    var placeSearch = new AMap.PlaceSearch({
      //构造地点查询类 pageSize: 12, pageIndex: 1, city: "成都", //城市 cityLimit: 'true', panel: 'temp'//搜索结果的展示面板对元素id,不知道为什么一定要有该参数,最终获取的结果才更完整,参数更多跟完整,//所以我在页面随便写了一个<div id="temp" style="display:none"></div>
    });
    //关键字查询
    placeSearch.search(txt, function(status, result) {
      if (status == "complete" && result.info == "OK") {
        //这里可以console.log(result),查看所有返回的参数,遍历展示这些基本的,我就不赘述
        //_this.searchData = result.poiList.pois
      }
    });
      });
    }
  }
};
</script>

5.骨架屏

我们希望在构建时渲染 skeleton 组件,将渲染 DOM 插入 html 的挂载点中,同时将使用的样式通过 style 标签内联。这样在前端 JS 渲染完成之前,用户将看到页面的大致骨架,感知到页面是正在加载的。

我们当然可以选择在开发时直接将页面骨架内容写入 html 模版中,但是这会带来两个问题:

开发 skeleton 与其他组件体验不一致。 多页应用中多个页面可能共用同一个 html 模版,而又有独立的 skeleton。 下面我们将看看插件在具体实现中是如何解决这两个问题的:

vue-skeleton-webpack-plugin

github 地址:https://github.com/lavas-project/vue-skeleton-webpack-plugin

具体实现步骤:

1、我们用 vue-cli 直接构建一下项目跑起来(具体怎么构建就不说了)

2、进去当前项目,执行命令 : npm install vue-skeleton-webpack-plugin

3、我们在 src 目录下创建 Skeleton.vue

<template>
  <div class="skeleton-wrapper">
    <header class="skeleton-header"></header>
    <section class="skeleton-block">
      <img
        src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg=="
      />
      <img
        src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg=="
      />
    </section>
  </div>
</template>

<script>
export default {
  name: 'skeleton'
}
</script>

<style scoped>
.skeleton-header {
  height: 40px;
  background: #1976d2;
  padding: 0;
  margin: 0;
  width: 100%;
}
.skeleton-block {
  display: flex;
  flex-direction: column;
  padding-top: 8px;
}
</style>

4、创建入口文件:entry-skeleton.js

import Vue from 'vue'
import Skeleton from './Skeleton'
export default new Vue({
  components: {
    Skeleton
  },
  template: '<Skeleton />'
})

5、我们在 build 目录下创建 webpack.skeleton.conf.js

'use strict'

const path = require('path')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const nodeExternals = require('webpack      - node-externals')

function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = merge(baseWebpackConfig, {
  target: 'node',
  devtool: false,
  entry: {
    app: resolve('../src/entry-skeleton.js')
  },
  output: Object.assign({}, baseWebpackConfig.output, {
    libraryTarget: 'commonjs2'
  }),
  externals: nodeExternals({
    whitelist: /\.css$/
  }),
  plugins: []
})

6.然后在 webpack.dev.conf.js 和 webpack.prod.conf.js 分别加入

const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin')
// inject skeleton content(DOM & CSS) into HTML
new SkeletonWebpackPlugin({
  webpackConfig: require('./webpack.skeleton.conf'),
  quiet: true
})

6.为什么要有 vue,从历史角度谈谈

因为 vue 大大提高了前端开发的效率,vue 带来了前端开发模式的转变,传统的前端开发是使用 html+css 搭建前端页面,使用 js 或 jquery 等技术进行页面的交互逻辑,然后使用 ajax 技术进行与后台的通信,后台返回数据浏览器进行页面的渲染。

使用 vue 之后我们可以进行数据的双向绑定,使用 vue-router 处理页面路由,使用 vue 处理各组件直接的通信,使用 vue-resource 或者 axios 处理 http 请求,使用 element-ui 或者 mint-ui 来搭建前端页面

  • 优点:目前路西的单页面流行框架,减轻服务器压力,前后端完全分离,比 augular 更省资源
  • 缺点:不利于 seo,(对国内搜索引擎来说,比如百度),第一次加载速度较慢
  • 解决:页面静态化,可以使爬虫抓取页面

7.vue 的核心内容:数据双向绑定 MVVM 思想 组件化开发 虚拟 DOM 树 自定义指令 生命周期

  • 为什么要有指令?操作 DOM,放到页面上
  • 1.为了把我们得到的数据放到页面上
  • 2.大牛把操作 DOM 方法封装,让程序员专注于操作数据

8.vue 的基本用法

8.1 常见指令

  • v-if 根据表达式的值在 dom 中生成或移除一个元素
  • v-show 根据表达式的值显示或隐藏 html 元素
  • v-if 和 v-show 都可以用来控制页面的隐藏和显示,他们的区别在于,v-if 是直接在页面中删除某个元素,v-show 是改变元素的 display 属性来控制隐藏或者显示
  • v-else 是 js 中 else 的意思,必须跟着 v-if 或者 v-show 使用
  • v-model 用来在表单控件元素上创建双向数据绑定
  • v-bind 用来响应更新 html 新特性,将一个或多个属性绑定到表达式上
  • v-on 用于绑定事件监视器
  • v-text 和 v-html 以及都是用来实现数据绑定的,v-text 和 v-html 的区别在于会不会输出 thml 标签 使用的话,在网速较慢的情况下会显示出来,可以使用 v-cloak 来解决这个问题

8.2 组件怎么做的?

在 vue 中定义全局组件有三种方式 1.使用 vue.extend({})定义全局组件,然后再通过 vue.component 把 vue.extend 定义的组件注册到 vue 全局上 2.直接用 vue.component("引用组件的名称”,{组件实际的定义模板}) 3.vue.component(“组件名称”,

8.3 用户怎么请求数据?

使用 vue-resource 或者使用 axios

getData(){this.$http
  .get("http://vue.studyit.io/api/ getnewslist").then(res =>{
						console.log(res.body);
						this.list = res.body.message
					})
					.catch(err =>{console.log(err);})
			},
postData(){this.$http
  .post("http://vue.studyit.io/api/postcomment/17",
  {content:"完美!"},
  {emulateJSON:true})
   .then(resp =>{
						console.log(resp.body)})}

8.4 用什么做路由

官方提供了 vue-router 插件,路由是浏览器中的哈希值(#hash)与展示视图内容(template)之间的对应规则 vue 中的路由是:hash 与 component 的对应关系

8.5 父组件给子组件传值

父--->子:用 props;(单项数据流)组件想要使用父组件的数据,我们需要通过子组件的 prop 选项来获得父组件传过来的数据,当父组件的 name 发生改变,子组件也会自动地更新视图,如果是改动了子组件中的 prop 的值得时候,父组件中的值是不会响应的变化的,如果要修改 prop,就把 prop 赋值给一个局部变量,然后需要修改的话就修改这个局部变量,而不影响 prop

8.6 子组件给父组件传值

子-->父:用$.emit(event ,[..args]) 父组件用$.on 接收,通过在父组件 $on(eventName) 监听自 定义事件,当子组件里 $emit(eventName) 触发该自定义事件的时候,父组件执行相应的操作方法。

8.7 兄弟组件间传值

兄弟-->兄弟 :用 vuex(状态管理模式)和 event.bus( 空实例对象)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>

  <body>
    <div id="app">
      <aaa></aaa>
      <bbb></bbb>
    </div>

    <script src="./vue.js"></script>
    <script>
      // 非父子组件通讯:

      var bus = new Vue()

      var vm = new Vue({
        el: '#app',
        data: {},

        // 通过 components 来创建属于该实例的局部组件
        components: {
          aaa: {
            template: `
        <div>
          <h1>组件A</h1>
          <button @click="fn">传递数据给组件B</button>
        </div>
      `,

            created() {
              bus.$emit('bfn', '组件A说:你好组件B')
            },

            methods: {
              fn() {
                // 触发事件,传递数据
                bus.$emit('bfn', '组件A说:你好组件B')
              }
            }
          },

          bbb: {
            template: `
        <div>
          <hr>
          <h1>组件B: {{ msg }}</h1>
        </div>
      `,

            data() {
              return {
                msg: ''
              }
            },

            // 在钩子函数中,绑定事件,这样以进入页面,事件就绑定好了,等待组件A触发
            created() {
              // 绑定事件,接受数据
              bus.$on('bfn', data => {
                this.msg = data
              })
            }
          }
        }
      })
    </script>
  </body>
</html>

9. vuex 里面有一个仓库是 state(数据仓库)改变数据仓库数据的一些方法 motation.异步的

和 motation(里面放同步的方法)

9.1 双向数据绑定的原理

答:vue 中数据的双向绑定采用的时候,数据劫持的模式。其实主要是用了 Es5 中的 Object.defineProperty;来劫持每个属性的 getter,和 setter

vue 执行的过程(双向数据绑定的原理解析):
  • 1 根据传入的 el 配置项,找到页面中需要被 Vue 管理的内容区域
  • 2 遍历所有的后代元素,收集出现的所有指令(v-model)和表达式()
  • 3 遍历传入 data 中的数据,分别通过 Object.defineProperty() 实现每个数据的 get/set
  • 4 每个数据的 get/和 set 中,分别与页面中使用该数据的指令和表达式对应起来
  • 5 将来当数据改变的时候,通过 设定好的 set,将数据的变化同步到页面中

9.2 路由的原理。

答:main.js 入口配置好路由,

在 app.vue 根组件中引入 router-view 标签,在 router 组件中配置各种路由 location.hash hashchange 事件

在访问 google plus 和网易 m 站时,细心的用户也许会发现页面之间的点击是通过 ajax 异步请求的,同时页面的 URL 发生了了改变。并且能够很好的支持浏览器的前进和后退。不禁让人想问,是什么有这么强大的功能呢?

HTML5 里引用了新的 API,就是 history.pushState 和 history.replaceState,就是通过这个接口做到无刷新改变页面 URL 的。

对应 history.pushState 和 history.replaceState 的具体用法可以参考下面这几篇文章:

HTML5 history 新特性 pushState、replaceState 传统的 ajax 的问题

虽然 ajax 可以无刷新改变页面内容,但无法改变页面 URL 为了更好的可访问性,内容发生改变后,改变 URL 的 hash。

但是 hash 的方式不能很好的处理浏览器的前进、后退等问题 有的浏览器引入了 onhashchange 的接口,不支持的浏览器只能定时去判断 hash 是否改变

ajax 的使用对搜索引擎很不友好,往往蜘蛛爬到的区域是空的 为了解决传统 ajax 带来的问题,HTML5 里引入了新的 API,即:history.pushState, history.replaceState 可以通过 pushState 和 replaceState 接口操作浏览器历史,并且改变当前页面的 URL。

9.3 数据请求的原理。

答:axios 原理也是 ajax。 3.3 vue2.0 源码的实现过程

10.工作中遇到的 bug?

浏览器兼容,移动端兼容问题

11.模块化与组件化

模块化开发以 js 文件功能分类, 组件化开发把 js,html,css 放到一个文件里,功能独立 vue~resource 支持 jsonp,vue2.0 以上不支持

12.axio 跨域问题怎么解决?

需要封装 jsonp,或者用 cros 服务器设置, 跨域:jsonp 支持低版本浏览器, cros 高版本浏览器,反向代理 服务器允许跨域的代码:

//允许所有的域名访问这个接口
header('Access-Control-Allow-Origin:*')
//允许www.study.com这个域名访问这个接口
header('Access-Control-Allow-Origin:http://www.jepson.com')
//vue获取更新后的数据

ajax 返回出现 error,datatype 没设置 模板引擎的原理是利用正则表达式定义标签替换{}语句中的内容 ,模板引擎的缺点不利于 seo,无法准确定位错误

h5css3 browersync 调试工具 httpcache 离线缓存

fiddler 拦截后端请求,模拟数据请求返回

测试前后端数据 get 请求直接在 url 地址栏,也可以用 fiddler,postmessage

13.协作测试 api 接口 ip 统一设置

const os = require('os')
var IPv4, net
const platform = os.platform()
switch (platform) {
  case 'darwin':
    // mac
    net = os.networkInterfaces().en0
    break
  case 'win32':
    // windows
    net = os.networkInterfaces()['以太网']
    break
  default:
    net = []
    break
}
for (var i = 0; i < net.length; i++) {
  if (net[i].family == 'IPv4') {
    IPv4 = net[i].address
  }
}
if (!IPv4) {
  IPv4 = 'localhost'
}

14.多行溢出样式打包问题

/*! autoprefixer: off */
-webkit-box-orient: vertical;
/* autoprefixer: on */

15.生命周期的具体使用

  • created 初始化静态数据
    • created 在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
  • mounted 初始化列表数据和搜索下拉数据 - mounted el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。 注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted

16.页面刷新 vuex 数据丢失


      // 在页面加载时读取sessionStorage里的状态信息
			const sessionStore = window.sessionStorage.getItem(storeKey.vuexStore)
			if (sessionStore) {
				let store = {}
				Object.assign(store, this.$store.state, JSON.parse(sessionStore))
				this.$store.replaceState(store)
				window.sessionStorage.removeItem(storeKey.vuexStore)
			}

			// 在页面刷新时将vuex里的信息保存到sessionStorage里
			// ie、谷歌、360 页面刷新执行顺序 onbeforeunload -> onunload -> onload,关闭执行顺序 onbeforeunload -> onunload
			// firefox 页面刷新只执行 onunload,页面关闭只执行 onbeforeunload
			let eventName = 'beforeunload'
			const fireFox = navigator.userAgent.indexOf('Firefox') !== -1
			if (fireFox) {
				eventName = 'unload'
			}
			window.addEventListener(eventName, () => {
				// 根据用户名是否存在判断是退出还是刷新
				const uname = this.$store.state.account.userInfo.uname
				if (uname) {
					window.sessionStorage.setItem(
						storeKey.vuexStore,
						JSON.stringify(this.$store.state)
					)
				}
上次编辑于: