vue基础

目录

  • vue
    • 一 、简介
    • 二、命令-生命周期函数
    • 三、v-model修饰符
    • 四、组件化
    • 五、es6模块化
    • 六、webpack
    • 七、vuecli
    • 八、runtime-only 和 runtime-compiler
    • 九、路由 vue-router
    • 十、打包js的结构图
    • 十一、keep-alive组件
    • 十二、自定义tab-bar
    • 十三、路径配置别名
    • 十四、promise
    • 十五、vuex
    • 十六、vxios
    • 十七、细节

vue

一 、简介

是什么

  • 是一个专注于视图的渐进式前端框架,如果需要其他功能可以使用它的拓展库,Vue 的核心库只关注视图层。

优缺点

  • 可以和其他语言共存,在一个app或者一个页面视图中,可以使用其他的语言开发,不会影响,因为他是组件化的

二、命令-生命周期函数

命令

order 语法糖 作用
v-bind=attr :attr 将数据和标签帮顶起来
v-on:action=actionName @action=actionName 绑定事件,事件不需要传参数可以省略()
{{expression} 从vue对象中取值
v-if="boolean" if
v-else-if="boolean" else if
v-else else
key 作为一种标签的身份标识
v-show="boolean" 和v-if的区别是,它是display=none标签还在
v-for="(item, index) in" for循环
:class="{className:boolean}" 也可以用数组
v-model="entity" 双向绑定表单,实际上是两个指令结合v-bind:value和v-on:input

生命周期函数

vue基础_第1张图片

  1. created():当组件创建成功时
    export default {
      name: "Home",
      created() {
        console.log("Home组件被创建成功");
      }
    };
  2. mounted(): 当组件被挂载到dom上
    export default {
      name: "Home",
      created() {
        console.log("Home组件被创建成功");
      },
      mounted(){
        console.log("组件被挂载成功")
      }
    };
  3. updated(): 当组件中发生变化时

    export default {
      name: "Home",
      created() {
        console.log("Home组件被创建成功");
      },
      mounted(){
        console.log("组件被挂载成功")
      },
      updated(){
        console.log("组件中发生改变时");
      }
    };

事件传参问题

  • 绑定事件时没写(),但是需要传参,参数为undefined
  • 绑定事件写了(),需要传参但是没有传,vue会默认传绑定的event对象给第一个参数,第二个就是undefined
  • 如果需要传event对象,只写event会被vue解析如果没又在vue中定义会报错,需要写成$event

事件的冒泡、捕获、其他属性

  • 冒泡:@click="action(123)" =>@click.stop="action(123)" , 解决冒泡
  • 捕获:@click="action(123)" =>@click.prevent="action(123)"
  • 键盘:@keyUp【常用】|keyDown.enter="action",监听enter键
  • 自定义组件的根元素的事件:@click.native
  • 只监听一次: @click.once

标签值被复用了

需要给标签添加一个key的属性就可以了,是因为虚拟dom的问题

v-for

  • 一般v-for中要指定:key=“一般是一个唯一的东西” 要和每一个标签对应起来,虚拟dom进行diff算法的时候会复用这个标签
 
    
  • 支持响应式的方法
    • pop()删除最后一个元素
    • push(a,b...)追加一【多】个元素
    • shift()删除第一个元素
    • unshift(a,b...)添加【多】元素到第一个的位置
    • sort([compare])
    • reverse()
    • splice(起始位置,删除几个【只填第一个参数就是从第一个位置删除到完】,从第一个参数位置添加【可以多个】的元素)
    • Vue.set(src,index,newValue) 修改src 中index位置值
  • 不支持响应式的方法
    • items[index]=""

过滤器

在vue的options中定义filters:{run :function(pram),调用 param|run

三、v-model修饰符

{{message}}

v-model.lazy

  • v-model 会和标签双向绑定,但是编辑时,时时刻刻都在同步,资源消耗没必要,v-model.lazy会监听enter 或者手标失去焦点的时候才进行数据同步
{{message}}

v-model.number

  • 将输入框的值转换为number类型,默认是字符串处理所有的键盘录入

v-model.trim

  • 将输入框中的左右两边的空格去掉

四、组件化

什么是组件

借鉴了将一个大的问题拆分成一个个的小问题这种思想

  • 将一个页面拆分成一个个的小组件,可以递归的拆分
  • 每个组件完成自己相关的功能,多个组件共同组成一个页面或者程序
  • 复用性:下次需要同样的功能就可以复用

组件的使用

  1. 创建组件的构造器
  2. 注册组件
  3. 使用组件

必须放在vue管理的作用域内,如果是多个标签必须被一个元素包裹,就是有一个唯一的祖先元素

局部组件

11
22

父子组件

11

组件的传递

组件不会向上级作用域传递,只会向下传递,孙子没有在爷爷的作用域注册的话孙子只能在父亲的作用域使用

组件的语法糖

11

模板的分离


11

组件访问数据

  • 组件不能访问实例中的数据
  • 只能访问自己的数据
  • data属性是一个function不是对象,可以返回一个数据对象供它访问
  • 组件也有method属性,它的原型实际上是指向vue的实例的
11

组件的data必须是函数

  • 如果写属性的话,很容易造成多个组件的数据引用指向同一块内存,会相互影响
  • 用function的话你只要每次返回一个匿名对象,他是没有公共引用指向的所以不会影响,如果需要的话你自己可以return 一个公用的引用就会相互影响的
  • 所以为了避免这种bug,data不是function就会报错
  • 必须return 一个对象{}

父子组件通信

父传子
  • props属性 : 可以写成数组或者对象,对象可以限制类型,对象更好点,也可以类型写成对象添加更多的限制、给默认值
  • 给默认值的时候如果是对象或者是数组,不能直接用{}、[] 需要用工厂来创建
  • 自定义validator
  • 可以自定义一个类作为类型
子传父|自定义事件
  • v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent 将会变成 v-on:myevent——导致 myEvent 不可能被监听到。
  • $emit --》this.$emit('myevent')会传递给父组件的监听事件要同名
  • 推荐你始终使用 kebab-case 的事件名 my-event
  • 子组件尽量和自己的data属性去绑定
练习
  1. num1、num2从父组件传递过来
  2. 修改num1,dnum1也变,同时传dnum1给父组件,父组件改变num1,也改变了prop1
  3. dnum2一直是dnum1的1%

watch

  • 监听对象不能直接监听,可以用computed代替

{{message}} {{demo.name}}
  • 如果是键的路径需要用引号包裹
  • 也可以外部调用
{{demo1.name}} {{demo.name}}

访问子组件实例 $children和$refs

  • 一般不会用$children来取子组件
  • $refs.refName|['refName']
    • 如果多个相同的引用会取最后一个
    • 如果绑定的是一个普通标签拿到的就是一个dom对象

访问父组件实例

  • 不建议使用this.$parent,会让组件的耦合增强不够独立
  • 祖先组件this.$root
具名插槽



作用域插槽
  • 父组件想要替换子组件的插槽的数据,数据的具体值还是由子组件来决定
  • slot-scope="slotData",类似于该组件的对象,2.5之前要用template标签



  
  
  
  Document



五、es6模块化

为什么有模块化

  • js是按顺序加载的,所以一般相互依的js是具有强制性的
  • 多个js文件定义的引用会污染全局变量,多人协作开发可能会有冲突
  • 可以用闭包

闭包解决多人协作开发

  • 只需要写好自己的模块化的命名,就可以很好的避免冲突了,相当于把所有的容错点都聚焦在一个点上,犯错的机会就少了,
  • 但是代码的复用性还是很差
// ;是为了防止其他的导入js相互影响
;var xm01 = (function xiaoming01() {
  return {
    aa:"asdas",
    flag: true
  };
}())


//js文件2
;(function () {
  if (xm01.flag) {
    alert("xm01.flag:" + xm01.flag);
  }
}());

六、webpack

webpack起步

  • 入口js

    //commonjs规范
    const {add} = require('./mathUtil.js');
    
    console.log(add(1,3));
    
    //es6规范
    import {result} from "./es6.js";
    
    console.log(result);
  • es6规范

const result = 45456;
export {result};
  • common规范
function add(a, b) {
  return a + b;
}

module.exports = {add};

webpack配置

  • 导出的时候es6和commonjs不能在一个模块中混用
  • 配置webpack.config.js:要使用commonjs规范
//node的包里面的path模块,用来拼接绝对路径
const path = require('path');

//这里要用commonjs导出,不能用es6
module.exports = {
  entry: './src/main.js',
  ouput: {
    //必须使用绝对路径
    path: path.resolve(__dirname,'dist'),
    filename: 'bundle.js'
  }
};
  • package配置:json不能有注释
{
  "name": "meetpackage",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    //npm run build 会在这个配置文件中找webpack命令,这个使用的是本地的命令,
    //不是全局的webpack,本地是针对于你的这个开发项目
    "build":"webpack"
  },
  "author": "",
  //开发的依赖
   "devDependencies": {
    "webpack": "^3.6.0"
  },
  //开源才需要这个,json中不能注释
  "license": "ISC"
}
  • 本地安装:开发时依赖 npm install [email protected] --save-dev
  • 终端terminal里敲的命令都是全局的
为什么使用--save-dev而不是--save?

--save 会把依赖包名称添加到 package.json 文件 dependencies 下;

--save-dev 则添加到 package.json 文件 devDependencies 键下;

webpack-loader

  • 官网可以找到对应的loader安装
    • 例:npm install style-loader[@version] --save -dev[表示开发环境用]
    • npm install babel-loader@7 babel-core babel-preset-es2015 --save-dev es6转es5
//node的包里面的path模块,用来拼接绝对路径
const path = require('path');

//这里要用commonjs导出,不能用es6
module.exports = {
  entry: './src/main.js',
  output: {
    //必须使用绝对路径
    path: path.resolve(__dirname,'dist'),
    filename: 'bundle.js',
    //为所有的url相关的添加路径
    publicPath:'dist/'
  },
  module:{
    rules: [
      {
        test: /\.css$/,
        // style-loader将模块的导出作为样式添加到 DOM 中
        // loader解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码
        // 从右到左的顺序加载
        use: [ 'style-loader', 'css-loader' ]
      },
      // {
      //   test: /\.(png|jpg|gif)$/,
      //   use: [
      //     {
      //       loader: 'url-loader',
      //       options: {
      //         //限制图片大小,大于limit会找file-loader
      //         limit: 9999
      //       }
      //     }
      //   ]
      // },
      // 在使用webpack进行打包时,对图片路径的处理方法常用的有两种,一种是file-loader,
      // 一种是url-loader,当我们使用其中一种是,请把另一种删掉,不然会出现图片无法正常显示的问题
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              //name是文件名,hash取8位,ext是拓展名
              name:'img/[name].[hash:8].[ext]'
            }
          }
        ]
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['es2015']
          }
        }
      }
    ]
  }
};

webpack-vue

  1. npm install vue -save

  2. 不写路径默认从node_modules引入 import Vue from 'vue'

  3. runtime-only:是运行的时候代码不能包含任意一个template标签

  4. runtime-compiler:代码中可以有template标签

    1. 解决3.4碰到的问题
     module:{
    
      resolve:{
        alias:{
          // vue$正则,表示导入的时候会检测vue指向的文件夹,如果这里不指定,会去找默认的runtime-only
          'vue$':'vue/dist/vue.esm.js'
        }
      }
//使用vue
import Vue from 'vue';


const App = {
    template: `
    

{{msg}}

`, data() { return { msg: 'hello world' }; } }; new Vue({ el: '#app', // template和el关系是,这里的template会替换el的标签 template: ``, components: { App } });
将组件的代码提出去
  • 新建一个.vue文件




  • npm install vue-loader vue-template-compiler --save -dev
    • 会出现版本过高的问题 安装一个低版本的
    • 编辑package.json中的版本号,会根据你的大版本找一个合适的,必须重新npm install

webpack-plugin

  • 安装打包静态文件:npm install --save-dev html-webpack-plugin
  • 压缩js文件替换变量为更简单的:npm install [email protected] --save -dev 指定的vueCli 2
webpack-dev-server
  • 全局安装:可以不用

    npm install webpack-dev-server -g 
  • 开发环境:

    npm install webpack-dev-server -save -dev
  • 配置参数:

    --content-base //设定webpack-dev-server的director根目录。如果不进行设定的话,默认是在当前目录下。
    --quiet: //控制台中不输出打包的信息,开发中一般设置为false,进行 打印,这样查看错误比较方面
    --no-info: // 不显示任何信息
    --colors: //对信息进行颜色输出
    --no-colors: //对信息不进行颜色输出
    --compress:  //开启gzip压缩
    --host : //设置ip
    --port : //设置端口号,默认是:8080
    --inline: //webpack-dev-server会在你的webpack.config.js的入口配置文件中再添加一个入口,
    --hot: //开发热替换
    --open: //启动命令,自动打开浏览器
    --history-api-fallback: //查看历史url
  • 两种方式:

    1. 直接scripts中使用:"dev": "webpack-dev-server --contentBase src --port 80 --hot --colors"
    2. 配置文件:
      plugins: [
        new webpack.BannerPlugin('最终版权是小明'),
        //打包静态资源,并且指定模板
        new htmlWebpackPlugin({
          template:`index.html`
        }),
        //压缩js
        new UglifyJsWebpackPlugin(),
        //热加载,不会全部加载,只加载改动的地方,配置了hot就需要配置,直接在命令中使用--hot就不需要配置这个插件
        // new webpack.HotModuleReplacementPlugin()
      ],
      // devServer: {
      //   contentBase: 'src',
      //   port: 80,
      //   hot:true
      // },
  • 报错可能是版本问题

webpack.config.js配置文件

//node的包里面的path模块,用来拼接绝对路径
const path = require('path');
const webpack = require('webpack');
const htmlWebpackPlugin = require('html-webpack-plugin');
const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin');

//这里要用commonjs导出,不能用es6
module.exports = {
  entry: './src/main.js',
  output: {
    //必须使用绝对路径
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    //为所有的url相关的添加路径
    // publicPath: 'dist/'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        // style-loader将模块的导出作为样式添加到 DOM 中
        // loader解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码
        // 从右到左的顺序加载
        use: ['style-loader', 'css-loader']
      },
      // {
      //   test: /\.(png|jpg|gif)$/,
      //   use: [
      //     {
      //       loader: 'url-loader',
      //       options: {
      //         //限制图片大小,大于limit会找file-loader
      //         limit: 9999
      //       }
      //     }
      //   ]
      // },
      // 在使用webpack进行打包时,对图片路径的处理方法常用的有两种,一种是file-loader,
      // 一种是url-loader,当我们使用其中一种是,请把另一种删掉,不然会出现图片无法正常显示的问题
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
            //name是文件名,hash取8位,ext是拓展名
              name: 'img/[name].[hash:8].[ext]'
            }
          }
        ]
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['es2015']
          }
        }
      },
      {
        test: /\.vue$/,
        use: {
          loader: 'vue-loader'
        }
      }
    ]
  },
  resolve: {
    // 这写拓展名可以省略
    extensions: ['.css', '.js', '.vue'],
    alias: {
      // vue$正则,表示导入的时候会检测vue指向的文件夹,如果这里不指定,会去找默认的runtime-only
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
  plugins: [
    new webpack.BannerPlugin('最终版权是小明'),
    //打包静态资源,并且指定模板
    new htmlWebpackPlugin({
      template:`index.html`
    }),
    //压缩js
    new UglifyJsWebpackPlugin(),
    //热加载,不会全部加载,只加载改动的地方,配置了hot就需要配置,直接在命令中使用--hot就不需要配置这个插件
    // new webpack.HotModuleReplacementPlugin()
  ],
  // devServer: {
  //   contentBase: 'src',
  //   port: 80,
  //   hot:true
  // },
};

抽取分离配置文件

  • 创建三个配置文件:
    1. base.config.js : 存放公共的配置
    //node的包里面的path模块,用来拼接绝对路径
    const path = require('path');
    const webpack = require('webpack');
    const htmlWebpackPlugin = require('html-webpack-plugin');
    
    //这里要用commonjs导出,不能用es6
    module.exports = {
        entry: './src/main.js',
        output: {
            //必须使用绝对路径
            path: path.resolve(__dirname, '../dist'),
            filename: 'bundle.js',
        },
        module: {
            rules: [
                {
                    test: /\.css$/,
                    // style-loader将模块的导出作为样式添加到 DOM 中
                    // loader解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码
                    // 从右到左的顺序加载
                    use: ['style-loader', 'css-loader']
                },
                {
                    test: /\.(png|jpg|gif)$/,
                    use: [
                        {
                            loader: 'file-loader',
                            options: {
                                //name是文件名,hash取8位,ext是拓展名
                                name: 'img/[name].[hash:8].[ext]'
                            }
                        }
                    ]
                },
                {
                    test: /\.js$/,
                    exclude: /(node_modules|bower_components)/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: ['es2015']
                        }
                    }
                },
                {
                    test: /\.vue$/,
                    use: {
                        loader: 'vue-loader'
                    }
                }
            ]
        },
        resolve: {
            // 这写拓展名可以省略
            extensions: ['.css', '.js', '.vue'],
            alias: {
                // vue$正则,表示导入的时候会检测vue指向的文件夹,如果这里不指定,会去找默认的runtime-only
                'vue$': 'vue/dist/vue.esm.js'
            }
        },
        plugins: [
            new webpack.BannerPlugin('最终版权是小明'),
            //打包静态资源,并且指定模板
            new htmlWebpackPlugin({
                template: `index.html`
            })
        ],
    };
    1. dev.config.js : 存放开发时配置

      const WebpackMerge = require('webpack-merge');
      const baseConfig = require('./base.config');
      
      module.exports = WebpackMerge(baseConfig, {
          devServer: {
              contentBase: 'src',
              port: 80,
              inline: true
          }
      });
    2. prod.config.js : 存放生产时配置

      const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin');
      const WebpackMerge = require('webpack-merge');
      const baseConfig = require('./base.config');
      
      module.exports = WebpackMerge(baseConfig, {
          plugins: [
              //压缩js
              new UglifyJsWebpackPlugin()
          ]
      });
      
  • 修改scripts
    • 可删除默认的webpack.config.js
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "webpack-dev-server --config ./build/dev.config.js",
        "build": "webpack --config ./build/prod.config.js"
      },
    

七、vuecli

  • 介绍:

    • vue command interface 命令行界面:使用一些命令搭建项目的基础结构
    • 都使用会规范很多配置,易维护,减少出错率
    • 依赖 webpack and npm-》npm-》依赖node
  • 安装:

    • npm install -g @vue/cli
  • 卸载之前版本

    • npm uninstall vue-cli -g / npm uninstall -g @vue/cli
  • 拉取v2的模板

    • npm install -g @vue/cli-init
      
v2使用:
  • 基于webpack3
创建project
vue init webpack projectName

vue基础_第2张图片

v3使用:
  • 零配置

  • 隐藏build和config目录,可以在node-modules/@vue/cli-service

    • 要修改配置需要根目录创建一个vue.config.js

      module.exports={};
      
  • 基于webpack4

  • 提供vue ui命令,图形化操作

  • 移除static,新增public目录将index.html移动到下面

创建project
vue create projectName
  1. vue基础_第3张图片

  2. vue基础_第4张图片

  3. vue基础_第5张图片

  4. 会默认创建一个.git文件夹

  5. 自定义配置:

    1. 根目录新建 vue.config.js
    module.exports = {
      configureWebpack: {
        resolve: {
          // extensions:[],
          //配置别名
          alias: {
            'assets': '@/assets',
            'components': '@/components',
            'network': '@/network',
            'common': '@/commom',
            'views': '@/views',
          }
        }
      }
    };
    
    
    1. 配置.editorconfig : 代码格式
    root = true
    
    [*]
    charset = utf-8
    indent_style = space
    indent_size = 2
    end_of_line = lf
    insert_final_newline = true
    trim_trailing_whitespace = true
    
    
vueUi
vue ui  打开图形管理界面
el
  • el:'#app'最后执行的还是$mount('#app')

八、runtime-only 和 runtime-compiler

  1. runtime-only:是运行的时候代码不能包含任意一个template标签

    • render(h)- 》 virtual Dom - 》UI真实dom
  2. runtime-compiler:代码中可以有template标签

    • template加载过程:

      template - 》parse - 》ast 抽象语法树 - 》compiler - 》render(h)- 》 virtual Dom - 》UI真实dom

    3比4性能更高,代码更少(少6kb)

//runtime-compiler
new Vue({
  el: '#app',
  components: { App },
  template: ''
})


//runtime-only,这个h是一个createElement('tagName',{attrName:'attrVal'},['innerHtml'])
//在vue中也可以传一个template对象靠vue-template-compiler解析成render(),也可以递归创建
new Vue({
  el: '#app',
  render: h => h(App)
})

九、路由 vue-router

一般使用vue的插件都要用Vue.use(插件)

  • 介绍:
    • 互联的网络将信息传输到目标地址的活动
    • 路由器提供两种机制:
      1. 路由:决定数据包从源到目的地的路径
      2. 将输入端的数据转送到合适的输出端
    • 路由表:是一个映射表,决定了数据包的指向
  • 后端路由--前端路由
    • 后端处理url和页面的映射,jsp是后端渲染,到前端的时候页面就确认好了
    • 前端处理url和页面的跳转映射

改编url不刷新页面

  • 改location.hash='aaa';
  • history:
    • 改history.pushState({},'','aaa');类似压栈,history.back()类似弹栈
    • 改history.replaceState({},'','aaa'),不能back()
    • 改history.go(-1) = history.back(),前进或者后退
    • 改history.forword()=history.go(1)
  1. 安装路由:npm install vue-router --save 因为生产也需要路由

  2. 导入:

    • router/index.js
    import Vue from 'vue'
    //1. 导入插件
    import Router from 'vue-router'
    import HelloWorld from '@/components/HelloWorld'
    
    //2. 使用插件
    Vue.use(Router)
    
    //3. 创建路由配置
    const routes = [
      {
        path: '/',
        name: 'HelloWorld',
        component: HelloWorld
      }
    ];
    
    //4. 传入路由配置,导出路由对象
    export default new Router({
      routes
    })
    
    
    • main.js
    import Vue from 'vue'
    import App from './App'
    //只写目录默认会找  index.js
    import router from './router'
    
    Vue.config.productionTip = false
    
    new Vue({
      el: '#app',
      router,
      render: h => h(App)
    })
    
    
  • 替换a标签
首页 详情
  • 常用属性

    • tag 、replace

      
          
          首页
      
    • 配置默认的active的样式

      .router-link-active{
        color: #f00
      }
      
      
    • 自定义样式:手动一个一个标签的写

      
          首页
      
    • 配置全局的active-class

    export default new Router({
      routes,
      mode:'history',
      linkActiveClass:'active'
    })
    

默认重定向

const routes = [
  {
    path:'/',
    redirect:'/home'
  },
  {
    path: '/home',
    name: 'Home',
    component: Home
  },
  {
    path:'/about',
    name:'About',
    component:About
  }
];

设置router的默认方式为history

  • 本身默认hash
  • history:url不会显示#号
//4. 传入路由配置,导出路由对象
export default new Router({
  routes,
  mode:'history'
})

手动写路由跳转

  • router会给每个组件传$router






动态路由

  1. 创建一个vue组件:User.vue

    
    
    
    
    
    
  2. 配置路由:index.js

    import Vue from 'vue'
    import User from '../components/User.vue'
    
    //1. 导入插件
    import Router from 'vue-router'
    
    //2. 使用插件
    Vue.use(Router)
    
    //3. 创建路由配置
    const routes = [
      {
        path:'/user',
        component:User
      }
    ];
    
    //4. 传入路由配置,导出路由对象
    export default new Router({
      routes,
      mode:'history',
      linkActiveClass:'active'
    })
    
    
  3. 加入路由到目标组件:Vue.vue

    
    
    
    
    
    
    
  4. 导入组件到入口 : main.js

    import Vue from 'vue'
    import App from './App'
    //只写目录默认会找  index.js
    import router from './router'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      render: h => h(App)
    })
    
    
  5. 设置动态路由:index.js

    import Vue from 'vue'
    import User from '../components/User.vue'
    
    //1. 导入插件
    import Router from 'vue-router'
    
    //2. 使用插件
    Vue.use(Router)
    
    //3. 创建路由配置
    const routes = [
      {
        path:'/user/:userName',
        component:User
      }
    ];
    
    //4. 传入路由配置,导出路由对象
    export default new Router({
      routes,
      mode:'history',
      linkActiveClass:'active'
    })
    
  6. 配置页面的url: Vue.vue

    
    
    
    
    
    
    
  7. 获取动态路由中的参数:User.vue

    • $route是当前活跃的路由
    
    
    
    
    
    

route 、router 、$router 、$route

  • $router:路由组件对象 配置的路由对象
  • $route:当前活跃路由

懒加载

  • 如果把所有的js都打包到app中,js将会很大,访问的时候会有等待时间,所以把不同的路由对应的组件分割成不同的代码块,然后当路由被访问的时候加载对应的资源,就更加高效了

    import Vue from 'vue'
    
    //替换成懒加载
    // import Home from '../components/Home.vue'
    // import About from '../components/About.vue'
    // import User from '../components/User.vue'
    
    //懒加载:
    const Home = ()=>import('../components/Home.vue')
    const About = ()=>import('../components/About.vue')
    const User = ()=>import('../components/User.vue')
    
    
    //1. 导入插件
    import Router from 'vue-router'
    
    //2. 使用插件
    Vue.use(Router)
    
    //3. 创建路由配置
    const routes = [
      {
        path:'/',
        redirect:'/home'
      },
      {
        path: '/home',
        name: 'Home',
        component: Home
      },
      {
        path:'/about',
        name:'About',
        component:About
      },
      {
        path:'/user/:userName',
        component:User
      }
    ];
    
    //4. 传入路由配置,导出路由对象
    export default new Router({
      routes,
      mode:'history',
      linkActiveClass:'active'
    })
    
    

    子路由

    index.js
    import Vue from 'vue'
    
    //替换成懒加载
    // import Home from '../components/Home.vue'
    // import About from '../components/About.vue'
    // import User from '../components/User.vue'
    
    //懒加载:
    const Home = () => import('../components/Home.vue')
    const About = () => import('../components/About.vue')
    const User = () => import('../components/User.vue')
    const HomeChild = () => import ('../components/HomeChild.vue')
    
    
    //1. 导入插件
    import Router from 'vue-router'
    
    //2. 使用插件
    Vue.use(Router)
    
    //3. 创建路由配置
    const routes = [
      {
        path: '/',
        redirect: '/home'
      },
      {
        path: '/home',
        name: 'Home',
        component: Home,
        children: [
          {
            path: '',
            // redirect:'child'
          },
          {
            //这里不能同/开头,会自动加上
            path: 'child',
            name: 'HomeChild',
            component: HomeChild
          }]
      },
      {
        path: '/about',
        name: 'About',
        component: About
      },
      {
        path: '/user/:userName',
        component: User
      }
    ];
    
    //4. 传入路由配置,导出路由对象
    export default new Router({
      routes,
      mode: 'history',
      linkActiveClass: 'active'
    })
    
    
    Home.vue
    
    
    
    
    
    
    

    传参到另一个组件

    profile.vue
    
    
    
    
    
    
    
    配置路由:index.js
    const Profile = () => import('../components/Profile') 
    
    {
        path: '/profile',
        component: Profile
      }
    
    显示位置的路由传参
    
    

为什么这样传参:

  • 符合url的规范:

  • 所以可以用query对象传参

导航守卫

全局守卫

所有的路由都会被过滤,也可以在特定的组件内创建局部守卫

  • 主要监听页面的跳转

  • from从哪个组件来的

  • to去跳转到哪个组件

  • next()

    • next(false)中断路由
    • next(path)跳转到哪个页面,可用来做一些条件判断的跳转,比如login
    • 其他的用的时候查官网
    import Vue from 'vue'
    
    //替换成懒加载
    // import Home from '../components/Home.vue'
    // import About from '../components/About.vue'
    // import User from '../components/User.vue'
    
    //懒加载:
    const Home = () => import('../components/Home.vue')
    const About = () => import('../components/About.vue')
    const User = () => import('../components/User.vue')
    const HomeChild = () => import('../components/HomeChild.vue')
    const Profile = () => import('../components/Profile')
    
    
    //1. 导入插件
    import Router from 'vue-router'
    
    //2. 使用插件
    Vue.use(Router)
    
    //3. 创建路由配置
    const routes = [
      {
        path: '/',
        redirect: '/home'
      },
      {
        path: '/home',
        name: 'Home',
        component: Home,
        meta: {
          title: '首页'
        },
        children: [
          {
            path: '',
            // redirect:'child'
          },
          {
            //这里不能同/开头,会自动加上
            path: 'child',
            name: 'HomeChild',
            component: HomeChild,
          }]
      },
      {
        path: '/about',
        name: 'About',
        component: About,
        meta: {
          title: '详情'
        },
      },
      {
        path: '/user/:userName',
        component: User,
        meta: {
          title: '用户'
        },
      },
      {
        path: '/profile',
        component: Profile,
        meta: {
          title: '档案'
        },
      }
    ];
    const router = new Router({
      routes,
      mode: 'history',
      linkActiveClass: 'active'
    })
    
    router.beforeEach((to, from, next) => {
      next()
      //匹配path中的meta对象的title
      document.title = to.matched[0].meta.title
      console.log(to);
      // console.log(from);
      // console.log("next: "+next);
    })
    
    //4. 传入路由配置,导出路由对象
    export default router
    
    
独享守卫
import Vue from 'vue'

//懒加载:
const Home = () => import('../components/Home.vue')

//1. 导入插件
import Router from 'vue-router'

//2. 使用插件
Vue.use(Router)

//3. 创建路由配置
const routes = [
  {
    path: '/',
    redirect: '/home'
  },
  {
    path: '/home',
    name: 'Home',
    component: Home,
    meta: {
      title: '首页'
    },
    children: [
      {
        path: '',
        // redirect:'child'
      },
      {
        //这里不能同/开头,会自动加上
        path: 'child',
        name: 'HomeChild',
        component: HomeChild,
        beforeEnter: (to, from, next) => {
          console.log("独享守卫");
            next()
        }
      }]
  }
];
const router = new Router({
  routes,
  mode: 'history',
  linkActiveClass: 'active'
})

//4. 传入路由配置,导出路由对象
export default router

前置钩子和后置钩子
  import Vue from 'vue'
  
  //懒加载:
  const Home = () => import('../components/Home.vue')
  
  //1. 导入插件
  import Router from 'vue-router'
  
  //2. 使用插件
  Vue.use(Router)
  
  //3. 创建路由配置
  const routes = [
    {
      path: '/',
      redirect: '/home'
    },
    {
      path: '/home',
      name: 'Home',
      component: Home,
      meta: {
        title: '首页'
      }
    },
    
  ];
  const router = new Router({
    routes,
    mode: 'history',
    linkActiveClass: 'active'
  })
  
  //前置钩子 hook,像filter一样
  router.beforeEach((to, from, next) => {
    next()
    //匹配path中的meta对象的title
    document.title = to.matched[0].meta.title
    console.log(to);
  })
  
  //后置钩子
  router.afterEach((to,from)=>{
    console.log("在跳转之后调用");
  })
  
  //4. 传入路由配置,导出路由对象
  export default router
  

十、打包js的结构图

  • app*.js所有的业务代码
  • mainifest*.js代码转换的依赖的底层支持
  • vendor*.js第三方插件
  • .map文件:项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错。有了map就可以像未加密的代码一样,准确的输出是哪一行哪一列有错,可以设置:config/index.js productionSourceMap:false

十一、keep-alive组件

  1. 是vue的一个组件:保证一些组件进入缓存不用你每次请求解析资源,提高效率,在显示的地方配置

    
    
  2. keep-alive的组件才可以使用activated()、deactivated()

    
    
    
    
    
    
    
  3. keep-alive 的exclude、include属性

    1. exclude=“componentName,componentName...”,被排除在缓存之外,不能加空格
    
          
        
    
    export default {
      name: "Profile",
      created() {
        console.log("profile created");
      },
      destroyed() {
        console.log("profile destroyed");
      }
    };
    

十二、自定义tab-bar

  1. /style中引用要用@import /

    准备好tabbar.vue,调好样式,预留出来一个插槽,用来放具体的tabbar的item

    
    
    
    
    
    
    
  2. 封装tab-bar-item

    
    
    
    
    
    
    
  3. 注册到app.vue中

    
    
    
    
    
    
    

    可以优化class,颜色直接写死不合适

    还可以从父组件传过来,然后绑定style来设置

十三、路径配置别名

vue-cli2

  1. 配置别名:\build\webpack.base.conf.js

      resolve: {
        extensions: ['.js', '.vue', '.json'],
        alias: {
          '@': resolve('src'),
          'assets': resolve('src/assets'),
          'components': resolve('src/components'),
          'views': resolve('src/views'),
        }
      },
    
  2. 项目中使用

    1. html中: 前面要加 ~

      
            首页
            
            
      首页
    2. import中使用

      import TabBarItem from "components/tabbar/TabBarItem";
      

vue-cli3

  1. 根目录下新建vue.config.js
  2. 在vue.config.js中的chainWebpack中配置config.resolve.alias.set('@', resolve('src')).set('components', resolve('src/components'));

十四、promise

是什么?

是异步编程的一种解决方案

什么时候使用异步呢?

  1. 网络请求
  2. 回调函数的时候

promise用法

  1. 构造器有一个参数,是函数,这个函数有两个参数都是函数
  2. resolve:异步请求成功调的函数,被调用之后会调用then()
  3. then:来处理业务代码,参数是一个函数,可通过resolve来传入data
  4. reject:异步失败的时候调的函数,也可以传输数据到catch
  new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('传入then 中的 data')
            }, 1500)
        }).then(data => {
            console.log(data);

            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    // resolve('内部的resolve')
                    reject('内部的reject')
                }, 1500)
            })
        }).catch(data => {
            console.log(data);
        })
  1. promise异步完成后会有三种状态

    1. pendding等待
    2. fullfill 完全满足
    3. reject 拒绝|次品
  2. promise的另一种写法

    1. then中也可以传两个函数,第一个是成功,第二个是失败
     new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('传入then 中的 data')
                    // reject('失败')
                }, 1500)
            }).then(data => {
                console.log(data);
            },reject => {
                console.log(reject);
            })
    
  3. 再简化
    1. new Promise(resolve) ==>Promise.resolve(data) ==> data
    2. throw 'msg'也会被catch()捕获
// new Promise(resolve) ==>Promise.resolve(data) ==> data
        //throw 'msg'也会被catch()捕获

        new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('第一层...')
            }, 1500)
        }).then(data => {
            console.log(data);
            return Promise.resolve('第二层...')
            // return Promise.reject('额鹅鹅鹅')
            throw 'dsadsa'
        }).then(data=>{
            console.log(data);
            return 'aaa'
        }).then(data=>{
            console.log(data);
        }).catch(err=>{
            console.log(err);
        })
  1. Promise.all([PromiseInstance...])
    1. 多个异步请求同时等待成功后才执行后续代码
    2. 像是java-juc的栅栏
Promise.all([
                new Promise((resolve, reject)=>{
                    setTimeout(()=>{
                        resolve('1111111')
                    },1000)
                }),
                new Promise((resolve, reject)=>{
                    setTimeout(()=>{
                        resolve('222222')
                    },2000)
                })
        ]).then(data=>{
            //1111111,222222
            console.log(data.toString())
        })

十五、vuex

介绍

  1. 是什么?
    1. 是为vue程序提供一个集中状态管理模式和库
    2. 充当应用程序中所有组件的特殊共享变量的集中存储
    3. 这些共享状态都是响应式的

vue基础_第6张图片

  1. vuex修改状态的流程

vue基础_第7张图片

  • 通过提交 mutation 的方式,而非直接改变 store.state.count,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。

    由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。

  • actions步骤可以省略,一般异步的操作放在actions中完成后放在mutations中

  • mutations只能是同步的操作,devtools监听不到异步操作

使用步骤

store用法

state用法

state中所有的已定义的属性都是响应式的,新加入的不被响应:因为属性初始化后,都被一个dep对象=【watcher,watcher..】监控,后面加入的不受监控

  1. npm install vuex --save
    
  2. 新建、src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

//1.安装,底层会调用Vuex.install
Vue.use(Vuex)

// 2.创建对象
const store = new Vuex.Store({
  state: {
    count: 0
  }, mutations: {
    //state必须传,默认会传进来
    increment(state) {
      state.count++
    }
  }, actions: {}, getters: {}, modules: {}
})

// 3.导出store对象
export default store

  1. main.js挂载插件
import Vue from 'vue'
import App from './App'
import store from "./store";

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  store,
  render: h => h(App)
})

  1. App.vue






  1. HelloVuex.vue






getters用法

有点像computed的概念

  1. App.vue






  1. store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

//1.安装,底层会调用Vuex.install
Vue.use(Vuex)

// 2.创建对象
const store = new Vuex.Store({
  state: {
    count: 0,
    persons: [
      {name: 'a', age: 12},
      {name: 'b', age: 23},
      {name: 'c', age: 32},
      {name: 'd', age: 24}
    ]
  }, mutations: {
    //state必须传,默认会传进来
    increment(state) {
      state.count++
    }
  },
  actions: {},
  //最多只能写两个参数时state,getters,默认会传进来
  getters: {
    more20Person(state) {
      return state.persons.filter(per=>per.age>20)
    },
    more20PersonCount(state,getters){
      // 这里不用写括号
      return getters.more20Person.length
    },
    //返回一个函数可以传动态的参数
    moreAgePerson(state){
      return (age)=>{
        return state.persons.filter(per=>per.age>age)
      }
    }

  },
  modules: {}
})

// 3.导出store对象
export default store

mutations
  1. 官方规定修改state只能用mutations
  2. 分为两部分函数名叫做字符串时间类型
  3. 代码块叫做回调函数
  4. 可以传多个参数第一个是store,后面的自定义叫做payload(负载 )
//store/index.js 
mutations: {
    //state必须传,默认会传进来
    increment(state) {
      state.count++
    },
    add(state,num){
      state.count +=num
    }
  }

//app.vue
methods:{
    increment(){
      this.$store.commit('increment')
    },
    add(num){
      this.$store.commit('add',num)
    }
  }

第二种提交风格

  1. 提交的参数会当成一个对象来取
inc(num){
      this.$store.commit({
        type:'inc',
        num
      })
    }
mutations: {
    //state必须传,默认会传进来
    increment(state) {
      state.count++
    },
    add(state,num){
      state.count +=num
    },
        //当成对象处理参数
    inc(state,payLoad){
      state.count +=payLoad.num
    }
  }
响应式操作
update(state){
      //响应式
      // state.persons.push({name:'e',age:99})
      //响应式
      // state.person={name:'f',age:101}

      //新加的属性不会被监控,只有在其他任意的属性变化一次后他会刷新一次
      // state.person.add=111
      // state.person['address']=222
      //删除一个对象的属性
      // delete state.person.age

      //vue set value是响应式的,key必须是字符串
      // Vue.set(state.person,'asd','vue set value是响应式的')
      Vue.delete(state.person,'age')
    }
mutations中方法的官方定义
  1. 避免写错,定义一个常量对象,在使用的文件中导入

  2. 定义

    [const](){}
    
//mutation-type.js
export const INCREMENT='increment'
export const ADD='add'
export const INC='inc'
export const UPDATE='update'


import {INCREMENT,ADD,UPDATE,INC} from "./mutation-type";

//app.vue
update(){
      this.$store.commit({
        type:UPDATE,
      })
    }


//index.js
mutations: {
    //state必须传,默认会传进来
    [INCREMENT](state) {
      state.count++
    },
    [ADD](state,num){
      state.count +=num
    },
    //当成对象处理参数
    [INC](state,payLoad){
      state.count +=payLoad.num
    },
    [UPDATE](state){
      Vue.delete(state.person,'age')
    }
  }



actions
  1. mutations的异步方法修改的数据,插件是跟踪不到的
  2. 所有的异步操作都应该放在actions中处理,处理后的回调放在mutations中处理
  3. 修改state的唯一途径就是mutations
  4. actions中的默认参数是上下文context(context=store)

action处理异步操作:

//app.vue
aUpdate(){
      // this.$store.dispatch('aUpdate',{
      //   msg:'参数信息',
      //  success:(data)=>{console.log(data)}
      // })

      //第二种方法,异步函数返回的promise对象
      this.$store.dispatch('aUpdate',{
        msg:'参数信息'
      }).then(res=>{
        console.log('完成异步操作');
        console.log(res);
      })
    }


//index.js
actions: {
    // aUpdate(context,payload) {
    //   // console.log('默认参数是上下文对象: ',context)
    //   setTimeout(function () {
    //     context.commit('aUpdate',payload)
    //   }, 1000)
    // }

    //第二种方式返回一个promise对象,在调用处可以使用
    aUpdate(context, payload) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          context.commit('aUpdate', payload)
          resolve(12312)
        }, 1000)
      })
    }

  }
modules
  1. 只在一个state中存数据可能因为数据量过大而臃肿,所以在modules中分多个模块
  2. 取值$store.state. modulesName .propertise
  3. 子模块:
    1. state:需要指定模块名,可以和父模块同名
    2. getters : 和父模块同名会报错,可以直接访问不需要指定模块名
    3. actions|mutations : 和父模块名字相同都会调用,先调用父模块的,所以不要定义相同的名字

显示:app.vue

 

-------------state--modules的内容---------

{{$store.state.a.name}}

{{$store.getters.getModuleA}}

{{$store.getters.getModuleA_add('age')}}

{{$store.getters.getModuleA_add_root}}

methods:{ moduleA() { this.$store.commit('aUpdate','模块a名字修改') }, asyncUpdateModuleA(){ this.$store.dispatch('asyncUpdateModuleA') } }

index.js

modules: {
    a:{
      //需要指定模块名,可以和父模块同名
      state:{name:'module_a',person:123},
      //和父模块同名会报错,可以直接访问不需要指定模块名
      getters:{
        getModuleA(state){
          return state.name+'_getModuleA'
        },
        getModuleA_add(state,getters){
          return (age) => {
            return getters.getModuleA+age
          }

        },
        //三个默认参数
        getModuleA_add_root(state,getters,rootState){
          return state.name+getters.getModuleA+'_add_'+rootState.count
        }
      },
      // 和mutations使用差不多
      actions:{
          //也可以使用对象的解构,详见es6
        asyncUpdateModuleA(context){
          setTimeout(()=>{
            context.commit('aUpdate','异步修改子模块')
          },1000)
        }
      },
      mutations:{
        //和父模块名字相同都会调用,先调用父模块的,所以不要定义相同的名字
        aUpdate(state,payload){
          state.name=payload
          console.log('child mutations 被调用')
        }
      },
      modules:{}
    },
        
    //模块b
    b:ModuleB
  }

抽离index.js

  1. state一般是不抽取出来的
  2. modules是新建一个./modules/文件夹,在里面建立模块
  3. 抽离好的文件
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from "./mutations";
import actions from "./actions";
import getters from "./getters";
import module_a from "./modules/module_a";

//1.安装,底层会调用Vuex.install
Vue.use(Vuex)

// 2.创建对象
const store = new Vuex.Store({
  state: {
    count: 0,
    persons: [
      {name: 'a', age: 12},
      {name: 'b', age: 23},
      {name: 'c', age: 32},
      {name: 'd', age: 24}
    ],
    person: {
      name: 'g',
      age: 100
    }
  },
  mutations,
  actions,
  getters,
  modules: {
    a: module_a
  }
})

// 3.导出store对象
export default store

十六、vxios

介绍

  1. 为什么选则它
    • vue作者不再维护vue-resource,推荐使用vxios
    • 可以在node环境中使用
    • 可以拦截请求
    • 可以拦截响应数据处理
  2. 支持的请求方式,满足resful,和jq的有点像
    1. axios(config) :默认是get,参数是一个对象
    2. axios.request(config)
    3. axios.get(url [, config])
    4. axios.delete(url [, config])
    5. axios.head(url [, config])
    6. axios.post(url [, data [, config] ])
    7. axios.put(url [, data [, config] ])
    8. axios.patch(url [, data [, config] ])
  3. 内部封装了promise

使用

初步使用
import axios from 'axios'

axios.defaults.baseURL = 'https://httpbin.org'
axios.defaults.timeout = 5000
axios({
  // url:'http://123.207.32.32:8080/home/mutidata',
  url: 'post',
  method: 'post',
  // 拼接在URL后
  params: {
    name: 1
  },
  // 请求体中的参数
  data: {
    type: 'sell',
    page: 3
  },
  //拦截请求
  transformRequest:[function (query) {

  }],
  //拦截返回数据
  transformResponse:[function (response) {

  }],
}).then(res => {
  console.log(res);
})

// 同时处理多个异步请求,最后返回一个数据数组,像java的栅栏
axios.all([axios({url: 'post', method: 'post'}), axios({url: 'get'})]).then(res => {
  console.log(res);
})

//处理返回的结果数组,使用的是数组的解构是根据下标解构的
axios.all([axios({url: 'post', method: 'post'}), axios({url: 'get'})])
  .then(([res1, res2]) => {
    console.log(res1);
    console.log(res2);
  })
// 这样也可以
axios.all([axios({url: 'post', method: 'post'}), axios({url: 'get'})])
  .then(
    axios.spread((res1, res2) => {
      console.log(res1);
      console.log(res2);
    }))

进一步封装

避免使用全局的axios,可能每个模块的请求是不一样的

  1. 使用的时候导入就可以了
  2. transformrequest和axiosInstance.interceptors.request.use 不冲突后者先调用
  3. transformResponse和axiosInstance.interceptors.response前者先调用

新建/network/request.js

import axios from "axios";

export function request(config) {
  if (!config.baseURL) {
    config.baseURL = 'https://httpbin.org'
  }
  if (!config.timeout) {
    config.timeout = 5000;
  }
  const axiosInstance = axios.create(config);
  //req是请求参数对象
  axiosInstance.interceptors.request.use(req => {
    console.log(req);
    //1.可以修改一些请求的参数
    // 2.可以设置一个加载图片
    return req
  })
  //res是返回的对象
  axiosInstance.interceptors.response.use(res => {
    console.log(res.data);
    return res.data
  })
  return axiosInstance(config);
}

十七、细节

  • this.$refs.[refName]只会取当前模块的引用
  • style标签里的scoped只会作用当前的组件的css
  • 组件是不能直接监听原生事件的,需要:@click.native=""

你可能感兴趣的:(vue基础)