vue学习笔记

第一站

初使用

js 中

// let(定义变量) / const(定义常量) 创建变量的方式
const app = new Vue({
    el:'#app',  // 用来挂载要管理的元素
    data:{  // 定义数据
        message: '你好呀!',
        name: 'hello 你好呀'
    },
    // 方法区,在HTML中可以调用此区域中的方法
    methods: {
        // 定义一个click方法
        click: function() {
            alert("你好")
        }
    },
    // 计算属性
    computed: {
        upChar(message) {
            message.up
        }
    }
    // 在网页被创建前执行此区域方法
    beforeCreate: function (){
        console.log("beforeCreate");
    },
    // 在网页完成创建后执行此区域方法
    created: function() {
        console.log("created");
    },
    // 
    mounted: function() {
        console.log("mounted");
    },
})

HTML 中

<!-- app为模块名称与 vue 中使用的 el 挂载名一致 -->
<div id="app">
    <!-- moustache语法:
			使用: {}来引入data中的数据
			moustache中可以使用简单的计算: + - * /
	-->
    <h2>{{message}}</h2>
    <h1>{{name}}</h1>
</div>
<!-- 导入 vue 模块 -->
<script src="../js/vue.js"></script>

初级语法

  • v-on: 用来绑定事件
    • 语法糖写法: " @ "
    • 例子:
      • v-on:click=“click”
      • @click=“click”
  • v-for="item in movies" 遍历数组moves中每一个数据, item 为每一个数据
  • v-html="url" 以html方式展示
  • v-text="message" 已文本方式展示
  • v-pre 将标签中的内容原封不动地展示出来
  • v-cloak 当vue代码执行后才会显示标签内容
  • v-once 修改后内容不会改变,只会显示第一次得值
  • v-bind 动态绑定
    • 语法糖写法: " : "
    • 例子:
      • v-bind:style=“color:blue”;
      • :style=“color:blue”;

组件

  • 创建组件(同样组件中也有自己的数据域,和vue实例一样该有的属性一样可以使用,但在组件中data属性必须为函数)

    const cpn1 = Vue.extend({
    	template: `
    		<div>
                <h1>组件1</h1>
    		</div>
    	`,
    	data() {
    		return {
    			message: "hello"
    		}
    	}
    })
    
  • 注册组件

    • 全局注册组件

      			// 组件名    组件实例
      Vue.component('my-cpn', cpn1)
      
    • 语法糖写法

      Vue.component('my-cpn', {
      	template: `
      		<div>
                  <h1>组件1</h1>
      		</div>
      	`
      })
      
    • 局部注册组件

      Vue({
      	el: "#app",
      	components: {
      		//组件名 组件实例
      		my-cpn: cpn1
      	},
      	data: {
      		message: "你好"
      	}
      })
      
  • 使用组件(创建的组件必须在Vue管理的实例内)

    <div id="app">
        <!--可以多次使用-->
        <my-cpn></my-cpn>
    </div>
    
  • 在组件中创建子组件

    Vue.extend({
    	template: `
    		<div>
    			<div>父组件</div>
    			<cpn1></cpn1>      
    		</div>
    	`,
    	components: {
    		cpn1: cpn1
    	}
    })
    
  • 将模板从js中抽取出来放到html标签中

    • 方法一:

      <!--将模板写到script标签中,script类型必须是:text/x-template-->
      <script type="text/x-template" id="cpn">
      	<div>我是组件</div>
      </script>
      
    • 方式二:

      <template id="cpn">
          <div>我是组件</div>
      </template>
      
  • 注册组件

    Vue.component("cpn", {
    	template: "#cpn"
    })
    

第二站

父子组件间通信

  • 向子组件中传递信息

    <body>
        <div id="app">
            <div>{{message}}</div>
        <!--子组件中接受信息的名称为hello, hello2,父组件中传递的数据信息名称为message,message2-->
        <!--如果没有使用v-bind(:)将会将字符串message和message2传给子组件,而非·父组件中的信息-->
            <cpn :hello="message" :hello2="message2" :child-message-info="info"/>
        </div>
    </body>
    
    <template id="cpn">
        <!--vue2中模板里面必须包含一个根标签-->
        <div>
            <div>我是子组件</div>
            <div>父组件中的信息:</div>
            <div>{{hello}}</div>
            <div>{{hello2}}</div>
            <div>{{childMessageInfo}}</div>
        </div>
    </template>
    
    <script>
        cpn = {
            template: "#cpn",
            // 用来存放父组件中传递的信息
            // 方式一:
            props: ['hello''hello2']
            // 方式二:(此时限制了传到子组件中的信息类型,这里为string类型)
            props: {
            	hello: string,
            	hello2: string
        	}
        	// 方式三:(此时不仅限制了类型,还会有默认值,当父组件没有传来信息时,将会使用默认值)
        	props: {
                hello: {
                    type: string,
                    default: "我是默认值"// 表示必须要传递信息
                    required: true
                }hello2: {
                    type: Array,
                    // 当默认值为:[]时将会报错
                    // default: []
                    // 类型是对象或者数组时,默认值必须是一个函数
                    default() {
    					return []
                    }
                }
                // 当子信息接收变量为驼峰标识变量时,须在别的使用地方添加“-”来表示
                childMessageInfo: {
                    type: Object,
                    default() {
                        return {}
                    }
                }
            }
        }
    
        new Vue({
            el: "#app",
            components: {
                cpn
            },
            data: {
                message: "你好,我是父组件"message2: "你好我是第二条信息",
                info: {
                	name: "张三",
                	age: 18
            	}
            }
        })
    </script>
    
  • 向父组件中传递信息

    <body>
        <div id="app">
            <!--将子组件发射事件的名称绑定监听函数,此处绑定的为childClick事件-->
            <!--vue2中不能解析驼峰式写法,但在vue脚手架中可以解析-->
            <cpn @item-click="childClick"></cpn>
        </div>
    </body>
    
    <template id="cpn">
        <div>
            <h1>我是子组件</h1>
            <!--当按钮被点击时,触发函数进行发送事件-->
            <button v-for="item in categories"
                    @click="btnClick(item)">{{item.name}}</button>
        </div>
    </template>
    <script>
    
        const cpn = {
            template: "#cpn",
            data() {
                return {
                    categories: [
                        {id: '01', name: '手机数码'},
                        {id: '02', name: '家用电器'},
                        {id: '03', name: '服装装饰'},
                        {id: '04', name: '实用小件'}
                    ]
                }
            },
            methods: {
                btnClick(item) {
                    // 发射事件(自定义事件),发射事件的名称为第一个参数,第二个参数为发送的内容
                    this.$emit('item-click', item)
                }
            }
        }
    
        new Vue({
            el: "#app",
            components: {
                cpn
            },
            date: {
                message: "父子组件通信之子传父"
            },
            methods: {
                // 该处默认接收的变量为子组件发送来的变量
                childClick(item) {
                    console.log("已接收", item)
                }
            }
        })
    </script>
    

watch

  • watch 用来监听属性的改变

    <body>
        <div id="app">
            <div>{{message}}</div>
        </div>
    </body>
    <script src="vue/vue.js"></script>
    <script>
        const app = new Vue({
            el: "#app",
            data: {
                message: "hello world"
            },
            watch: {
                // 此处watch函数名必须为 data 中的数据名,其中newVal和oldVal分别为新值和旧值
                message(newVal, oldVal) {
                    console.log(newVla, oldVal)
                }
            }
        })
    </script>
    

父子组件的访问方式

父组件访问子组件:

c h i l d r e n : t h i s . children:this. childrenthis.children是一个数组类型,它包含所有子组件对象

​ $refs

子组件访问父组件:

​ $parent

  • 父组件调用子组件中的方法

    <body>
        <div id="app">
            <!--通过ref属性来在组件中使用该子组件-->
            <cpn ref="cpn1"></cpn>
            <cpn ref="cpn2"></cpn>
            <button @click="btnClick">调用自组件方法</button>
        </div>
    </body>
    
    <template id="cpn">
        <div>子组件</div>
    </template>
    <script>
    
        const cpn = Vue.component("cpn", {
            template: "#cpn",
            data() {
                return {
                    message: "我是子组件中的信息"
                }
            },
            methods: {
                showMSG() {
                    alert("调用成功!")
                }
            }
        })
    
        new Vue({
            el: "#app",
            components: {
                cpn
            },
            data: {
                message: "hello world"
            },
            methods: {
                btnClick() {
                    // 一般不使用$children属性,使用$refs来替代
                    this.$children[0].showMSG()
                    alert(this.$children[0].message)
                    // 推荐使用此方法
                    this.$refs.cpn1.showMSG()
                    console.log(this.$refs.cpn2.message)
                }
            }
        })
    </script>
    
  • 子组件调用父组件中方法

    <body>
        <div id="app">
            <cpn></cpn>
        </div>
        <template id="cpn">
            <div>
                <div>我是子组件</div>
                <button @click="btnClick">调用父组件中方法</button>
            </div>
        </template>
    </body>
    <script>
        const cpn = Vue.component("cpn", {
            template: "#cpn",
            methods: {
                btnClick() {
               // 使用$parent属性进行访问父组件,但此方法不建议使用(由于耦合度较高,父组件可能有多个)
                    this.$parent.showMSG()
                    console.log(this.$parent.message);
                    // 访问根组件使用$root,在此示例中$parent和$root一样
                    console.log(this.$root.message)
                }
            }
        })
    
        new Vue({
            el: "#app",
            components: {
                cpn
            },
            data: {
                message: "你好,我是父组件"
            },
            methods: {
                showMSG() {
                    alert("访问成功!")
                }
            }
        })
    </script>
    

插槽

在结构相似的功能组件中使用插槽能带来很大的便利

  • 定义插槽

    <body>
        <div id="app">
            <cpn><button>通过插槽引入button按钮</button></cpn>
            <cpn><a>通过插槽来添加a标签</a></cpn>
            <cpn></cpn>
        </div>
        <template id="cpn">
            <div>
                <div>子组件</div>
           <!--当组件中含有多个插槽时,在使用时都为以名子使用,则组件中插槽都将会被替换为组件中的内容-->
                <slot></slot>
                <!--此插槽含有默认值,当引入此插槽时,未在组件中添加任何元素,则使用默认添加到元素,如果有元素,则默认元素将会被替换为指定元素-->
                <slot><button></button></slot>
            </div>
        </template>
    </body>
    <script>
        const cpn = {
            template: "#cpn"
        }
    
        new Vue({
            el: "#app",
            components: {
                cpn
            }
        })
    </script>
    
  • 具名插槽

    <body>
        <div id="app">
            <!--在替换时使用slot属性来替换具体名字的插槽,其插槽的作用域只在某个模板下起作用,此处input将不会显示,由于isShow在vue实例中为false故不会显示-->
            <cpn><input v-show="isShow" slot="center"/></cpn>
        </div>
        <template id="cpn">
            <div>
                <!--在使用有名字的插槽时,必须选定要替换插槽的名字-->
                <slot name="left"><span>左边</span></slot>
                <slot name="center"><span>中间</span></slot>
                <slot name="right"><span>右边</span></slot>
            </div>
        </template>
    </body>
    <script>
        const cpn = {
            template: "#cpn",
            data() {
                return: {
                    isShow: true
                }
            }
        }
    
        new Vue({
            el: "#app",
            components: {
                cpn
            },
            data: {
                isShow: false
            }
        })
    </script>
    
  • 在父组件中引用子组件中的数据

    <body>
        <div id="app">
            <cpn></cpn>
            <cpn>
               <!-- 在2.5.x以前必须使用template模板,在2.5.x以后取消了这种约束,在模板中引用数据 -->
                <template slot-scope="slot">
                    <span v-for="item in slot.data">{{item}} --- </span>
                </template>
            </cpn>
            <cpn>
                <!-- 其中slot-scope为固定写法,后面的值为引用的子组件 -->
                <div slot-scope="sl">
                    <!-- 从子组件中的data域取值 -->
                    <a href="#" v-for="item in sl.data">  --{{item}}--  </a>
                </div>
            </cpn>
            
            <cpn2></cpn2>
            <cpn2>
                <div slot-scope="s">
                    <!--join函数将数组中间使用 - 来进行分割-->
                    <!-- 由于子组件中使用了message来存放变量故,在引用时也必须使用message -->
                    <i>{{s.message.join(' - ')}}</i>
                </div>
            </cpn2>
        </div>
        <template id="cpn">
            <div>
                <div>我是子组件</div>
                <!--data可以为自己取的名字,可以将data改为别的变量名称,在引用时也必须用别的变量名-->
                <slot :data="arr">
                    <ul>
                        <li v-for="item in arr">{{item}}</li>
                    </ul>
                </slot>
            </div>
        </template>
        <template id="cpn2">
            <div>
                <div>我是子组件2</div>
                <slot :message="arr">
                    <ul>
                        <li v-for="item in arr">{{item}}</li>
                    </ul>
                </slot>
            </div>
        </template>
    </body>
    <script>
        new Vue({
            el: "#app",
            components: {
                cpn: {
                    template: "#cpn",
                    data() {
                        return {
                            arr: ['java', 'c', 'c++', 'php']
                        }
                    }
                },
                cpn2: {
                    template: "#cpn2",
                    data() {
                        return {
                            arr: ['西红柿', '番茄', '南瓜', '冬瓜']
                        }
                    }
                }
            }
        })
    </script>
    

在 vue-cli3 及以上版本需使用以下方法进行插槽的使用

<!-- 这里使用 v-slot: 的形式来指定具名插槽 -->
<template v-slot:header>
	<h1>Here might be a page title</h1>
</template>

<template v-slot:footer>
	<p>Here's some contact info</p>
</template>

生命周期

模块化开发

  • commonjs语法

    • a.js

      let name = "小明"
      let age = 18
      
      let sayHello = function () {
          console.log("hello:" + name)
      }
      
      modlue.export = {
          name,
          age,
          sayHello
      }
      
    • b.js

      let name = "小红"
      let age = 19
      
      let sayHello = () => {
          console.log("hello:" + name)
      }
      
      modlue.exports = {
          name,
          age,
          sayHello
      }
      
    • main.js

      let {name, age, sayHello} = require("./a.js")
      
    • 在主页面中使用

      <!--在引入时加入type属性将不会发生命名冲突的问题-->
      <script src="main.js" type="module"></script>
      <script src="a.js" type="module"></script>
      <script src="b.js" type="module"></script>
      
  • ES6语法

    • a.js

      let name = "小明"
      let age = 18
      
      let sayHello = (msg) => {
          console.log("hello, " + msg)
      }
      
      // 导出方式一:
      export { name, age, sayHello }
      // 导出方式二:(定义变量时,直接导出)
      export let num = 1
      // 导出函数
      export function sum(num1, num2) {
          return num1 + num2
      }
      // 导出类
      // ES5语法
      export function Person() {
          let name
          let age
      }
      // ES6语法
      export class Person{
          let name
          let age
      }
      
      // (以上这些导出方式导出时为什么的名字,在别的文件中导入也只能用导出时的名字)
      // 通过export default 在导出时可以不命名,在一个文件中只能有一个export default。在导入时,可以重命名
      export default Perosn
      
    • b.js

      import {name, age, sayHello} from './a.js'
      // 此时导入了导出文件的默认导出数据,可以在导入时重命名
      import presonA from './a.js'
      // 此时导入为存放到一个obj的对象中,访问时可以使用”obj.“的形式
      import * as obj from './a,js'
      
      console.log(obj.name)
      
      (() => {
          console.log(name + "同学的年龄为:" + age)
      })()
      
      sayHello(name + "、小名:")
      

webpack进行打包

初使用

在打包时使用指令 webpack ./src/main.js -o ./dist/

webpack 在4.0以上要使用 -o 选项来进行指定输出地址,在之前版本不需要 -o 选项。并且不需要指定输出文件的名字,如果指定则会以文件夹的形式显示,会默认生成一个 main.js 的文件

在打包时,只需指定入口文件即可,别的 js 文件则不需进行打包

webpack配置

webpack的配置文件名称固定为 webpack.config.js

在配置的时候需要引入node的相关包来动态获取路径需要在终端执行 npm init 指令,生成一个 package.json 文件

  • webpack.config.js

    // 通过引入node的模块获取绝对路径
    const path = require('path')
    
    module.exports = {
        // 导入的入口
        entry: './src/main.js',
        // 导出的出口
        output: {
            // 要求path必须为绝对路径,path的resolve函数可以动态获取路径
            // __dirname为node的全局变量,join函数可以用来拼接两个路径,此处为该文件所处的绝对路径和 'dist' 路径进行拼接
            // 在webpack4.0以前使用resolve函数进行拼接路径
            path: path.join(__dirname, 'dist'),
            filename: "bundle.js"
        }
    }
    
  • package.json

    在json文件中不允许有注释代码片段

    {
      "name": "meet",
      "version": "1.0.0",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        // 在此处构建了一个映射,此时只需执行build指令即可执行打包(在终端执行 npm run build)
        // 此处定义了一个指令(build)该指令执行时,将会优先在本地进行查找本地不存在则会取去全局找
        // 在命令窗口敲的指令都会去全局找
        "build": "webpack"
      },
      "author": "",
      "license": "ISC",
      "description": ""
    }
    
    • 此时只需在终端敲 webpacknpm run build 命令,即可实现自动打包
  • 开发时依赖: npm install webpack@3.6.0 --save-dev

  • 运行时依赖: npm install vue --save

  • 自动生成的 package.json 文件

    • package.json

      {
        "name": "meet",
        "version": "1.0.0",
        "main": "index.js",
        "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1",
          "build": "webpack --mode development",
            // 配置webpack服务启动指令,参数:--open设置自启动
          "server": "webpack-dev-serve --open"
        },
        "author": "",
        "license": "ISC",
        "description": "",
          // 开发时依赖,通过--save-dev指令下载的包将会保存于此
        "devDependencies": {
          "webpack": "^3.6.0"
        },
          // 运行时依赖
        "dependencies": {
        
        }
      }
      
webpack loader
  • 下载loader以及loader配置

  • 使用 npm install 包名 --save-dev 来下载对应的loader,然后再webpack.config.js中添加相应配置

  • 再main.js中依赖相关的 css 文件或别的 js 文件即可,只需要打包完成后引入打包后的js文件即可,而不需要在index.html中引入css或别的js文件

  • webpack.config.js

    // 通过引入node的模块获取绝对路径
    const path = require('path')
    
    module.exports = {
        // 导入的入口
        entry: './src/main.js',
        // 导出的出口
        output: {
            // 要求path必须为绝对路径,path的resolve函数可以动态获取路径
            // __dirname为node的全局变量,resolve函数可以用来拼接两个路径,此处为该文件所处的绝对路径和 'dist' 路径进行拼接
            path: path.join(__dirname, 'dist'),
            filename: "bundle.js"
        },
        module: {
            rules: [{
                // 使用正则表达式进行匹配,匹配到符合条件的将会使用下列两个loader
                test: /\.css$/,
                // css-loader 只负责加载
                // style-loader 负责将样式添加到 DOM 中
                // 使用多个 loader 时,从右向左加载
                use: ['style-loader', 'css-loader']
            }, {
                test: /\.less$/i,
                use: [
                    // compiles Less to CSS
                    "style-loader",
                    "css-loader",
                    "less-loader",
                ],
            }]
        }
    }
    
webpack.config.js 详细配置

由于直接引入vue的时候可能会报错:提示你正在使用的时 runtime-only 此模板不能编译template,您应该使用 compiler-included。解决此问题,需在webpack.config.js中配置vue信息

// 通过引入node的模块获取绝对路径
const path = require('path')
// 导入webpack的包,后续可以引入webpack的插件
const webpack = require("webpack")
// 导入自动生成index.html插件
const HtmlWebPackPlugin = require('html-webpack-plugin')
// 导入自动丑化js代码插件
const uglifyJsPlugin = require("uglifyjs-webpack-plugin")

module.exports = {
    // 导入的入口
    entry: './src/main.js',
    // 导出的出口
    output: {
        // 要求path必须为绝对路径,path的resolve函数可以动态获取路径
        // __dirname为node的全局变量,resolve函数可以用来拼接两个路径,此处为该文件所处的绝对路径和 'dist' 路径进行拼接
        path: path.join(__dirname, 'dist'),
        filename: "bundle.js",
        // 当文件打包后通过css或别的文件引入的文件将会存在路径错误,此时需要配置一个公共地址,在别的文件加载时,会自动拼接公共地址
        publicPath: "dist/"
    },
    module: {
        rules: [{
            // 使用正则表达式进行匹配,匹配到符合条件的将会使用下列两个loader
            test: /\.css$/,
            // css-loader 只负责加载
            // style-loader 负责将样式添加到 DOM 中
            // 使用多个 loader 时,从右向左加载
            use: ['style-loader', 'css-loader']
        }, {
            test: /\.less$/i,
            use: [
                // compiles Less to CSS
                "style-loader",
                "css-loader",
                "less-loader",
            ],
        }, {
            test: /\.(png|jpg|gif|jpeg|webp)$/i,
            use: [{
                loader: 'url-loader',
                // 限制由url引入的文件的大小,此处单位为字节,此处大小为8kb
                options: {
                    // 当加载的图片小于limit时,会将图片编译成base64字符串形式
                    limit: 1000,
                    // 该处为打包后的文件设置文件命名方式 "[]" 表示变量的意思,此处为打包后的文件存放到img文件夹下,名字为原来文件的名称.8为hash值.扩展名
                    name: 'img/[name].[hash:8].[ext]'
                }
            }],
        }, {
            test: /\.(png|jpe?g|gif|webp)$/i,
            use: [{
                loader: 'file-loader'
            }],
        }, {
            // 此loader为将es6转化为es5的loader
            test: /\.m?js$/,
            // exclude排除,以下文件将不会被使用此loader
            exclude: /(node_modules|bower_components)/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env']
                }
            }
        },{
            test: /\.vue$/,
            use: ['vue-loader']
        }]
    },
    resolve: {
        // 使用此配置能省去文件的扩展名
        extensions: ['.js', '.vue', '.css'],
        // alias:别名,此时引入vue的时候将会在指定的文件夹下寻找vue,添加此配置时,将会解决template不能被加载的问题
        alias: {
            'vue$': 'vue/dist/vue.esm.js'
        }
    },
    plugins: [
        // 此插件能在每个打包后文件中添加版权说明
        new webpack.BannerPlugin("最终版权归小明所有"),
        // 此插件将会在打包后自动生成index.html文件,此时将不需要再配置publicPath路径
        new HtmlWebPackPlugin({
            // 根据index.html模板生成index.html文件
            template: "index.html"
        }),
        // 使用丑化插件,在打包后将自动丑化js代码,以进行压缩js文件
        new uglifyJsPlugin()
    ],
    // 该配置只在开发时需要,发布时可以删除
    devServer: {
        // 指定服务的文件夹
        contentBase: "./dist",
        // 指定是否要实时监听
        inline: true,
        // 指定服务器端口,默认为8080端口
        port: 8080
    }
}
  • 当vue实例中既有el属性又有template属性时,template将会替换掉el所挂载的所有东西

  • App.vue

    <template>
        <h1>{{message}}</h1>
    </template>
    
    <script>
    export default {
        name: "App",
        data() {
            return {
                message: "hello world"
            }
        }
    }
    </script>
    
    <style scoped>
        body {
            background-color: green;
        }
    </style>
    
  • main.js

    // 引入vue模块
    import Vue from 'vue'
    
    // 导入组件
    import App from './vue/App'
    
    new Vue({
        el: "#app", 
        // 此时template模板将会替换el所挂载的内容
        template: "<App/>",
        components: {
            // 组件挂载
            App
        }
    })
    
  • webpack.config.js文件的分离

    • base.config.js基本配置

      // 通过引入node的模块获取绝对路径
      const path = require('path')
      
      // 导入webpack的包,后续可以引入webpack的插件
      const webpack = require("webpack")
      
      // 导入自动生成index.html插件
      const HtmlWebPackPlugin = require('html-webpack-plugin')
      
      module.exports = {
          // 导入的入口
          entry: './src/main.js',
          // 导出的出口
          output: {
              // 要求path必须为绝对路径,path的resolve函数可以动态获取路径
              // __dirname为node的全局变量,resolve函数可以用来拼接两个路径,此处为该文件所处的绝对路径和 'dist' 路径进行拼接
              // 当webpack配置文件分离后需要修改打包路径在第二个参数前添加 ../
              // path: path.join(__dirname, 'dist'),
              path: path.join(__dirname, '../dist'),
              filename: "bundle.js",
              // 当文件打包后通过css或别的文件引入的文件将会存在路径错误,此时需要配置一个公共地址,在别的文件加载时,会自动拼接公共地址
              publicPath: "dist/"
          },
          module: {
              rules: [{
                  // 使用正则表达式进行匹配,匹配到符合条件的将会使用下列两个loader
                  test: /\.css$/,
                  // css-loader 只负责加载
                  // style-loader 负责将样式添加到 DOM 中
                  // 使用多个 loader 时,从右向左加载
                  use: ['style-loader', 'css-loader']
              }, {
                  test: /\.less$/i,
                  use: [
                      // compiles Less to CSS
                      "style-loader",
                      "css-loader",
                      "less-loader",
                  ],
              }, {
                  test: /\.(png|jpg|gif|jpeg|webp)$/i,
                  use: [{
                      loader: 'url-loader',
                      // 限制由url引入的文件的大小,此处单位为字节,此处大小为8kb
                      options: {
                          // 当加载的图片小于limit时,会将图片编译成base64字符串形式
                          limit: 1000,
                          // 该处为打包后的文件设置文件命名方式 "[]" 表示变量的意思,此处为打包后的文件存放到img文件夹下,名字为原来文件的名称.8为hash值.扩展名
                          name: 'img/[name].[hash:8].[ext]'
                      }
                  }],
              }, {
                  test: /\.(png|jpe?g|gif|webp)$/i,
                  use: [{
                      loader: 'file-loader'
                  }],
              }, {
                  // 此loader为将es6转化为es5的loader
                  test: /\.m?js$/,
                  // exclude排除,以下文件将不会被使用此loader
                  exclude: /(node_modules|bower_components)/,
                  use: {
                      loader: 'babel-loader',
                      options: {
                          presets: ['@babel/preset-env']
                      }
                  }
              },{
                  test: /\.vue$/,
                  use: ['vue-loader']
              }]
          },
          resolve: {
              // 使用此配置能省去文件的扩展名
              extensions: ['.js', '.vue', '.css'],
              // alias:别名
              alias: {
                  'vue$': 'vue/dist/vue.esm.js'
              }
          },
          plugins: [
              // 此插件能在每个文件中添加版权说明
              new webpack.BannerPlugin("最终版权归小明所有"),
              // 此插件将会在打包后自动生成index.html文件
              new HtmlWebPackPlugin({
                  // 根据index.html模板生成index.html文件
                  template: "index.html"
              })
          ]
      }
      
    • dev.config.js开发时依赖配置

      const webpackMerge = require("webpack-merge")
      const baseConfig = require("./base.config")
      
      module.exports = new webpackMerge(baseConfig, {
          devServer: {
              // 指定服务的文件夹
              contentBase: "./dist",
              // 指定是否要实时监听
              inline: true,
              // 指定服务器端口,默认为8080端口
              port: 8080
          }
      })
      
    • pro.config.js项目发布时依赖

      const webpackMerge = require("webpack-merge")
      
      // 导入自动丑化js代码插件
      const uglifyJsPlugin = require("uglifyjs-webpack-plugin")
      const baseConfig = require("./base.config")
      
      module.exports = (baseConfig, {
          plugins: [
              // 使用丑化插件,在打包后将自动丑化js代码,以进行压缩js文件
              new uglifyJsPlugin()
          ]
      })
      
    • 此时需要在package.json文件中添加webpack.config.js文件的路径,否则报错找不到webpack的配置文件

      "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1",
          // --config 指定路径
          "build": "webpack --mode development --config ./build/pro.config.js",
          "dev": "webpack-dev-serve --config ./build/dev.config.js"
        }
      

第三站

vue-cli

如果使用 vue - cli 2 的版本时,使用命令:vue init webpack 项目名 创建

vue - cli 高版本创建命令:vue create 项目名 创建

render函数

vue执行原理:

​ runtime - complier 执行原理:

​ tempalte --> ast(abstract tree) --> render --> virtual dom --> UI

​ runtime - only 执行原理:

​ render --> virtual dom --> UI

// 使用render函数创建对象
render: function (createELement) {
    retun createELement('div',
     {class: 'box'},
     ['我是由render函数渲染出来的标签', createELement('button', {id: 'btn'}, ['按钮'])])
  }


// 使用render函数传入组件对象
const cpn = {
    template: '<div>{{message}}</div>',
    data() {
        return {
            message: "我是组件"
        }
    }
}
render: function (createELement) {
    retun createELement(cpn)
  }

在vue3中,配置文件默认隐藏了,如果想要引入自己的配置,则需要创建:vue.config.js 文件,该文件名固定

vue.config.js

module.export = {
    ...
}

Vue - Router

前奏:

通过一些方式改变 url 但页面不发生刷新

​ 使用 location.hash = ‘a’; 可以将地址栏后面加 a 即原始为 www.baidu.com 则可以转变为:www.baidu.com/a

​ 使用 history.pushState({}, ‘’, ‘a’) 能达到和 hash 一样的效果,通过此方法是将新加的连接放入到堆栈中,之后在使用 history.back() 函数,则会一层一层返回

  • 参数:
  • data
  • title
  • url

history.replaceState({}, ‘’, ‘b’) 通过此方式则原来的url将会被覆盖不能通过 back() 函数返回到上一个页面

history.back() 回到上一个页面

history.go()

  • history.go(-1) 和 history.back() 等价
  • history.go(-2) 将会使栈中弹出两个地址,即返回两次
  • history.forword() 等价于 history.go(1)
  • history.go(2) 将会压进栈中两个,即前进两次地址
vue-router 配置

在 src 目录下创建 router 文件夹,在该文件夹中创建 index.js 文件,在该文件中配置路由相关信息

router – > index.js(vue3及以下使用方式)

// 导入vue在后续中用于注册组件
import Vue from 'vue'
// 导入路由,用于配置路由相关信息
import VueRouter from 'vue-router'

// 安装插件
Vue.use(VueRouter)

// 导入组件
import App from '../App.vue'
import About from '../components/About'
import Profile from '../components/Profile.vue'

// 配置路径相关变量
const routes = [
    {
        path: '',
        // 使用重定向,使页面刚开始时即定位到 /home 路径
        redirect: '/home'
    },{
		path: '/about',
		component: About
    },{
        // 通过 /:userId 来动态拼接 url 路径,在 Profile 组件中可以使用:this.$route.params.userId 获取传入的 userId 信息
		path: '/profile/:userId',
		component: Profile
    }]

// 创建路由对象
const router = new VueRouter({
    routes,
    // 改变路径的默认模式,默认模式为hash(该模式会使路径中含有 # ),此处改边为 history 模式
    mode: 'history',
    // 更改为点击后的 router-link 渲染之后的标签的 class 名字
    linkActiveClass: 'active'
})

// 导出router
export default router

router – > index.js(vue3以上使用方式)

// 导入路由相关对象
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import Home from '../views/Home.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

在 App.vue 中需指引到该页面的方式

<template>
  <div id="app">
      <!-- router-link 和 router-view 标签为router自动注册的组件,直接使用即可 -->
    <!-- 使用 router-link 标签,该标签在渲染后将会默认变为 a 标签 -->
    <router-link to="/about">关于</router-link>
    <!-- 使用 tag 属性将渲染之后的结果变为 button 标签,replace 属性使改变路径时使用的是替换而不是压栈,即不能返回 -->
    <!-- 当点击某个router-link渲染之后的标签后,vue 会自动给改变前添加两个 class 类(router-link-active 和 router-link-exact-active),可以通过该类名用于设置点击之后的样式,使用 active-class属性可以更改该标签被点击后的 class 属性,此处更改为 active ,可以在 router 配置文件中统一进行更改-->
    <router-link to="/profile" tag="button" replace active-class="active">我的</router-link>
      
      <!-- 通过方法进行更改页面的 url 进行页面跳转 -->
      <button @click="aboutClick">关于</button>
      <button @click="profileClick">我的</button>
      
      <!-- 动态拼接路径,在将要跳转后面加上动态的路径拼接信息 -->
      <router-link to="/profile/xiaoming">我的</router-link>
      <!-- 动态拼接路径,在将要跳转后面加上动态的路径拼接信息。此处为在 data 中寻找userId变量 -->
      <router-link :to="'/profile/' + userId">我的</router-link>
      
      <!-- router-view 组件将会使 router-link 标签中的指向渲染到此处,即替换router-view标签 -->
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
      return {
          userId: 'xiaoming'
      }
  }
  methods: {
      // 监听事件的点击
      aboutClick() {
          // router 为每个组件都注入了一个 $router 属性,该属性可以进行页面的跳转
          this.$router.push('/about')
      },
      profileClick() {
          // 通过replace方式进行页面跳转,该方法跳转页面将不能进行返回
          this.$router.replace('/profile')
      },
  }
}
</script>

在子组件中获取用户拼接的 url 路径可以通过过 this.$route.params.userId(userId为设置在路由中的动态变量)来获取 此时 $route 表示处于活跃的路由对象

通过路由的懒加载机制进行划分 js 文件,这样用户在初次访问时,将不会出现短暂的空白无响应状态,一个懒加载将会对应打包后的一个 js 文件

路由的懒加载三种方式

  • 方法一(结合 vue 的异步组件和 webpack的代码分析)不推荐:
const routes = [
  {
    path: '/',
    component: Home = resolve => {require.ensure(['../components/Home.vue'], () => {resolve(require('../components/'))} )}
  }
]
  • 方法二(AMD 写法):
const routes = [
  {
    path: '/',
    component: Home = resolve => require(['../components/Home.vue'], resolve}
  }
]
  • 方式三(ES6)推荐:
const routes = [
  {
    path: '/',
    component: Home = () => import ('../components/Home.vue')
  }
]

// 更优做法
const Home = () => import ('../components/Home.vue')

const routes = [
  {
    path: '/',
    component: Home
  }
]
  • 组件进行进一步细分,进行配置子路由
const Home = () => import('../components/Home.vue')
const News = () => import('../components/News.vue')
const Fun = () => import('../components/Fun.vue')

const routes = [
  {
    path: '/',
    component: Home,
    // 通过 children 属性来决定组件中子组件路由相关信息,子路由不需要加 ‘ / ’
    children: [
      {
          path: 'news',
      	  component: News
      },{
          path: 'fun',
          component: Fun
      }
    ]
  }
]
  • 在父组件中,和配置普通路由一样进行添加 router-link 和 router-view 标签设置子组件的显示位置及何时显示
<template>
  <div id="app">
      <!-- 在此处要添加父组件的路径 -->
      <router-link :to="'/home/news">新闻</router-link>
      
      <router-view/>
  </div>
</template>
  • 通过另一种方式进行界面的跳转以及参数的传递
<template>
  <div id="app">
      <router-link :to="{path: '/profile', query: {name: 'xiaoming', age: 18}}">新闻</router-link>
      
      <router-view/>
      
      <!-- 通过 query 来获取当前传的参数对象 -->
      <div>{{$router.query}}</div>

</template>
  • 通过普通标签进行设置跳转及参数传递
<template>
  <div id="app">
    <button @click="aboutClick">关于</button>
    <button @click="profileClick">我的11</button>
      
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
      return {
          userId: 'xiaoming'
      }
  }
  methods: {
      // 监听事件的点击
      aboutClick() {
          // router 为每个组件都注入了一个 $router 属性,该属性可以进行页面的跳转
          this.$router.push('/about')
      },
      profileClick() {
          // 通过replace方式进行页面跳转,该方法跳转页面将不能进行返回
          this.$router.push({
              path: '/profile',
              query: {
                  name: 'xiaoming',
                  age: 18
              }
          })
      },
  }
}
</script>

router 和 route不为同一个,route 代表当前活跃的那个路由对象,而 router 代表整个路由对象

扩展:

  • 为一个对象添加一个属性:

    const obj = {
        name: 'xiaoming'
    }
    
    // 通过 Object.defineProperty() 方法为对象进行添加属性(该方法为响应式)
    	// 参数一为要添加属性的对象,参数二为添加的属性名,参数三为属性值
    Object.defineProperty(obj, 'age', 18)
    
  • 在页面跳转时,该表网页的 title 名

    // 在组建被创建后将会调用此方法,然而此方式须在每个组件中都是用,故不推荐
    created() {
        // 通过此方法能改变每个网页的 title 名
        document.title = "用户"
    }
    
    // 更优做法,在路由配置文件中添加以下代码
    
    const routes = [
      {
        path: '/',
        component: Home = () => import ('../components/Home.vue'),
        // 为每一个路由对象添加元素据,用于跳转页面时更换 title 名
        meta: {
          title: '首页'
        }
      }
    ]
    
    // 这些方法也可在每一个映射关系中直接写入
    // 前置守卫
    router.beforeEach((to, from ,next) => {
        // from 和 to,即从 from 跳转到 to
        // 当有路由嵌套时,父路由将会找不到title属性,此时通过 matched 属性中的 meta 的 title 来设置
        document.titile = to.matched[0].meta.title
        
        // next 为下一步的意思,必须调用这个函数,当不调用此函数时,将会不发生页面的调转
        // next 函数中可以传递参数,当参数为 false 时,中断当前导航,或传入一个路径,即为跳转到该路径
        next()
    })
    
    // 后置钩子
    router.afterEach((to, from) => {
        // 后置钩子中没有 next 参数,即当跳转完页面之后才会调用此方法
    })
    

    使用 keep-alive 标签嵌套 router-view 标签,能做到,不频繁创建和销毁(由于组件之间进行切换时,会频繁创建和销毁操作),而是缓存下来,这样将会大大提高响应时间

  • keep-alive 的两个属性

    <!-- include 字符串或正则表达式,只有匹配的组件会被缓存 -->
    <!-- exclude 字符串或正则表达式,任何被匹配的组件都不会被缓存 -->
    <!-- 此处未不缓存 Home 和 Profile 组件,其中 exclude 属性的值未每个组件中的 name 属性值,多个组件使用 , 进行分割,之间不能加空格 -->
    <keep-alive exclude="Home,Profile">
    	<route-view/>
    </keep-alive>
    
    
    <!-- 此处未只缓存 Home 和 Profile 组件,其中 include 属性的值未每个组件中的 name 属性值,多个组件使用 , 进行分割,之间不能加空格 -->
    <keep-alive include="Home,Profile">
    	<route-view/>
    </keep-alive>
    
  • 当页面进行跳转时,设置返回原来页面时,于未跳转之前显示一样

    export default {
        name: 'home',
        data() {
            return {
                // 使用 path 变量用来记录上一次处于活跃状态的路径,用以以后跳转回来时使用
                path: '/home/news'
            }
        },
        // 当页面发生跳转,即路由发生转换前进行调用此函数
        beforeRouteLeave(to, from , next) {
            this.path = this.$route.path
            next()
        },
        // 当页面处于活跃状态时,进行调用此函数
        activated() {
            this.$router.push(this.path)
        }
    }
    
  • activated 和 deactivated 生命周期函数

    export default {
        name: 'home',
        
        // 只有当组件处于 keep-alive 即被保持了状态后才会有效,否则将不会自动调用这两个函数
        // 当页面处于活跃状态时,进行调用此函数
        activated() {
            console.log('activated')
        },
        deactivated() {
            console.log('deactivated')
        }
    }
    
在文件中配置路径映射关系
  • 在vue2版本时,找到 webpack.base.config.js 文件(即 webpack 配置文件)在里面修改 resolve 添加相应路径映射关系
resolve: {
    alias: {
        '@': resolve('src')
        'assets': resolve('src/ssets'),
        'components': resolve('src/components'),
        'views': resolve('src/views')
    }
}
  • 由于在vue3隐藏了相关的配置文件,故需要在根目录下创建一个 vue.config.js 文件(每次修改配置文件都需要重启以下项目),在该文件中写入以下信息,进行配置相关路径映射关系
module.exports = {
	configureWebpack: {
		resolve: {
			alias: {
				'assets': '@/assets',
				'components': '@/components',
				'views': '@/views'
			}
		}
	}
}

在非 import 方式导入时(例如:导入资源文件图片等)需要在前面加上” ~ “例:~assets/img/1.jpg

Promise 的使用
  • 由于异步请求网络数据,可能会导致回调地狱,使用 Promise 能简化编程,使代码看起来更加清晰,代码只需写在 then 函数中即可
new Promise((resolve, reject) => {
    // 使用 setTimeout 来模拟异步操作
    setTimeout(() => {
        //调用 resolve 函数
        resolve()
    }, 1000)
}).then(() => {
    // 在这里进行代码逻辑结构的处理
    console.log("hello xiaoming")

    // 返回一个 Promise 对象,在以后的调用
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            // 在 resolve 函数中可以进行传递参数,在 then 中可以接受该参数
            resolve("hello xiaohong")
        }, 1000)
    })
    // then 中的参数为调用 resolve 函数时,传来的参数
}).then((data) => {
    console.log(data)

    return new Promise((resolve, reject) => {
        setTimeout(() => {
            // 成功的时候调用 resolve,失败的时候调用 reject 函数
            reject("err ^ ... ^")
        } ,1000)
    })
    // 只有调用 resolve 函数时,才会执行 then 函数,当调用 reject 函数时,将会执行 catch 函数
}).then(() => {
    console.log("hello xiaogang") // 次语句将不会被执行
}).catch((err) => {
    console.log(err)
})  // 该程序执行结果为,每隔一秒输出一句打印语句
  • Promise 的另一种使用形式
new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("hello xiaogang")
        // reject("err ^ ... ^")
    }, 1000)
    // 在 then 函数中,可以传递两个参数,一个是数据返回结果,一个是错误信息,即在 then 函数中可以同时完成数据信息返回和错误信息打印
}).then(data => {
    console.log(data)
}, err => {
    console.log(err)
})
  • Promise 的多层级调用,单层级异步简写
new Promise((resolve, reject) => {
    // 使用 setTimeout 来模拟异步操作,在网络请求只有该阶段使用
    setTimeout(() => {
        resolve("hello xiaoming")
    }, 1000)
}).then((data) => {
    // 在这里进行代码逻辑结构的处理
    console.log(data)

    // 返回调用 resolve 函数后的结果,此处无异步处理,可直接调用 resolve函数,在每次调用时都修改数据的值,在以下调用中进行输出打印
    return Promise.reject(data + "1") // 该处调用了 reject 函数,即以下的 then 将不会执行
    // 还可以直接使用 throw 来抛出异常
    // throw 'error'
}).then((data) => { // 此函数将不会执行
    console.log(data)

    return Promise.resolve(data + "2")
}).then(data => { // 此函数将不会执行
    console.log(data)
}, err => { // 此函数将会执行
    console.log(err)
}) // 打印结果为 xioaming \n xiaoming1
  • Promise 进一步的简写
new Promise((resolve, reject) => {
    // 使用 setTimeout 来模拟异步操作,在网络请求只有该阶段使用
    setTimeout(() => {
        resolve("hello xiaoming")
    }, 1000)
}).then((data) => {
    // 在这里进行代码逻辑结构的处理
    console.log(data)

    // 返回数据处理后的结果,无需再调用 Promise.resolve() 函数
    return data + "1"
}).then((data) => {
    console.log(data)

    return data + "2"
}).then(data => {
    console.log(data)
}, err => {
    console.log(err)
})
  • Promise.all() 方法
// Promise.all 方法需传入一个数组,每个数组用于封装不同的网络请求
Promise.all([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("小明")
        }, 2000)
    }),
    
    new Promise((resolve, reject) => {
        setTimeout(() => {
        	resolve("小红")
        }, 1000)
    })
    // 该 data 对象为一个数组,该数组用于存放两个异步处理操作结果
]).then(data => {
    console.log(data[0])
    console.log(data[1])
}) // 结果为 小明 \n 小红

第四站

Vuex 状态管理器

当多个组件想使用同一个状态(变量)时,可以使用 vuex 来把这个状态存储到状态管理器中,这样,多个组件使用时可以直接从状态管理器中拿(相当于一个全局变量的存储容器)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WKp5GoUB-1626776658179)(D:\Dahui\DesktopApp\学习资料\学习笔记\前端\JavaScript遗忘知识点\Vue.js\Vue.js学习笔记\img\flow.png)]

Vuex的相关配置

在 src 文件夹下创建一个问价夹名为:store(仓库的意思,用于存储各种状态),在该问价夹中创建 index.js 文件

index.js(vue2.x)

// 导入 Vue 在后续中注册组件
import Vue from 'vue'
// 导入 Vuex 在后续中创建该对象
import Vuex from 'vuex'

// 注册该插件
Vue.use(Vuex)

// 创建 Vuex 对象
const store = new Vuex.Store({
    // state 对象用于保存状态(变量),在别的地方只需使用this.$store.state.变量名,即可访问该变量
    state: {
  	},
    // vuex 的 store 状态更新的唯一方式通过 mutation
  	mutations: {
    },
    actions: {
    },
    // 类似于组件中的计算属性,在每次使用状态时如果希望在更改后在使用,则需在此处进行实现
    getters: {
    },
    // 用于划分模块
    modules: {
    }
})

// 导出创建后的 store 对象
export default store

在 main.js 中注册该对象

main.js

import Vue from 'vue'
import App from './App'
import store from './store/index.js'

new Vue({
    el: '#app',
    store,
    render: h => h(App)
})

在 vue3.x 中使用以下方法

  • index.js(vue3.x)
import { createStore } from 'vuex'

export default createStore({
  // 在 state 中用于存放用于共享的状态
  state: {
      count: 100
  },
  mutations: {
      // 通过此方法进行对 count 变量进行修改,改方法会自动传入一个参数 state 用于访问 state 中的变量
      decreasement(state) {
          state.count --
      },
      increasement(state) {
          state.count --
      }
  },
  actions: {
  },
  modules: {
  }
})

在 main.js 中注册该对象

  • main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

createApp(App).use(store).use(router).mount('#app')
修改状态管理器中的状态

在修改状态管理器中的状态时如果使用 this.$store.state.变量名 = 值 时,在后期调试无法得知时哪个组件对该状态进行了修改,为了方便后期的维护应使用官方给出的方式去更改

  • 该图中显示了更改状态管理器中的方式
  • Devtools 为浏览器中进行调试 vue 的插件,Mutions 只能处理同步的操作,对于异步请求等操作不能染过 Action环节,否则 Devtools 无法跟踪到

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hRdMiLDQ-1626776658183)(D:\Dahui\DesktopApp\学习资料\学习笔记\前端\JavaScript遗忘知识点\Vue.js\Vue.js学习笔记\img\vuex.png)]

通过以下方法进行状态的修改

  • App.vue
<template>
  <div id="app">
    <div>{{$store.state.count}}</div>
    <div>{{$store.getters.powerCounter}}</div>
    <div>{{$store.getters.length}}</div>
    <!-- 在此处传入参数进行修改含有参数的状态 -->
    <div>{{$store.getters.mergeS('a')}}</div>

    <button @click="additionCount(5)">+5</button>
    <button @click="addition">+</button>
    <button @click="substract">-</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    addition() {
      // 通过提交的方式进行调用状态管理器中的函数,参数为要调用的函数
      this.$store.commit('increment')
    },
    substract() {
      this.$store.commit('decrement')
    },
    additionCount(count) {
        // 通过传入第二个参数的形式可以在调用 mutations 方法中传递参数
        this.$store.commit("increasementCount", count)
        // 特殊的提交方式,当使用这种方式传递参数时,传递过去的实则为一个对象,访问时需使用 参数.count 
        this.$store.commit({
            type: 'increasementCount',
            count
        })
    }
  }
}
</script>
  • index.js(store文件夹中的 vuex 配置文件)
import { createStore } from 'vuex'

export default createStore({
  state: {
    count: 100,
    obj: {
        age: 18
    }
  },
  // 通过此对象中的函数进行对状态进行修改
  mutations: {
    // 在改方法中被调用时会自动出入 state 对象,该对象为用户所定义的状态
    increment(state) {
      state.count ++
    },
    decrement(state) {
      state.count --
    },
    increasementCount(state, count) {
        state.count += count
    },
    update(state) {
        // 在 2.x 版本中此属性将不会加入响应式系统中,而在高版本中此功能已修复,可以进行响应式修改
        state.obj['name'] = "xiaoming"
        // 在 vue 中可以使用 Vue.set() 方法为对象添加属性,该属性能加入响应式系统中,参数一为要修改的对象,参数二为添加到属性,参数三为属性值
        Vue.set(state.obj, 'name', 'xiaoming')
        // 使用 delete 关键字能删除对象中的某个属性,但该方法做不到响应式
        delete state.obj.age
        // 而使用 Vue.delete() 方法能响应式删除对象中某个属性,参数一为要修改的对象,参数二为删除的属性(Vue.2x)
        Vue.delete(state.obj, age)
    }
  },
  getters: {
      // 此处可以在使用时直接使用修改过后的数据,在使用时可以直接使用 $store.getters.powerCount 来访问
      powerCounter(state) {
          return state.count * state.count
      },
      // 此方法会默认传入一个 getters 该 getters 为此处定义的 getters 
      length(state, getters) {
          return getters.powerCounter >= 10000 ? 10 : getters.powerCounter
      },
      mergeS(state) {
          // 调用该方法时返回一个函数,在该函数中可以传入函数,该函数可以通过传入的参数返回修改后的状态
          return function(num) {
              return state.count + num
          },
              // 更为简洁的写法,但不推荐,因为不好看懂
              mergeS(state) {
              return num => state.count + num
          }
      }
  },
  actions: {
      // 定义一个方法,该方法中会默认传递一个参数,该参数为创建的该 store 对象,参数二为传入的参数
      aUpdate(context, payload) {
          // 在修改store对象中的状态时,只能通过 mutations 修改 store 对象中的参数
          // 在异步操作执行时,通过 actions 来执行 mutations 中的方法,使用此方式 devtools 才能动态跟踪到 store 对象中的状态的变化,在执行 actions 中的方法时,使用 this.$store.dispatch("aUpate")
          setTimeout(() => {              
          	  context.commit("increment")
          }, 1000)
      },
      // 一种提醒外面,该处已经修改成功的方法,参数二为外提交时传递的一个函数: this.$store.dispatch("aUpate", () => {console.log("里面已经修改完成")})
      aUpdate(context, payload) {
          setTimeout(() => {              
          	  context.commit("increment")
              // 调用该函数
              payload()
          }, 1000)
      },
      // 另一种方式,在该方式中传递参数时使用如下:this.$store.dispatch("aUpate", {message: '我是信息', success: () => {console.log("里面已经修改完成")}})
      aUpdate(context, payload) {
          setTimeout(() => {              
          	  context.commit("increment")
              // 调用该函数
              payload.success()
          }, 1000)
      },
      // 另一种更加优雅方式
      aUpdate(context, payload) {
          retrun new Promise((resolve, reject) => {
              setTimeout(() => {              
                  context.commit("increment")
                  console.log(payload)
                  resolve("传递过去的信息")
              }, 1000)
          })
      }
      // 在别处调用该方法时代码如下
      this
      	.$store.dispath("aUpdate", "携带过去的信息")
    	.then(res => {
    		console.log(res)
		})
  },
      
      
  moduleA = {
    state: {
        name: "xiaoming"
    },
  	mutations: {
        updateName(state, payload) {
            state.name = payload
        }
    },
    actions: {
        // 此处的 context 为该模块对象
        updateName(context) {
            // 在此处使用 commit 时,提交的为该模块中的 mutations 中的方法
            context.commit("updateName", 'xiaogang')
        }
    },
    getters: {
        update(state) {
        	return state.name + "1"
    	},
        // 此处 getters 就为该 getters
        update2(state, getters) {
        	return getters.update + "1"
    	},
        // 此处 getters 就为该 getters,rootState 为根 store 对象
        update3(state, getters, rootState) {
            // 可以通过 rootState 访问根 store 对象的状态
        	return getters.update + rootState.count
    	}
    },
    modules: {}
  }

  moduleB = {
    state: {},
  	mutations: {},
    actions: {},
    getters: {},
    modules: {}
  }
  // 在该对象中可以定义模块,模块中又可以定义 state 及别的属性,可以无限套娃,在使用时:this.$store.moduleA.name,在调用 mutations 中的方法时:this.$store("updateName", "xiaohong"),所以各个模块中的 mutations 不要重复
// 在使用 getters 中的方法时也是直接使用:this.$store.getters.update,故getters中定义的方法也不能重复
  modules: {
      moduleA: moduleA
      moduleB: moduleB
  }
})

官方推荐,将除 state 和 modules 外的属性抽取到外部文件,在 index 中只需要导入即可,modules 中的相应模块抽取到新建的 modules 文件夹中

  • 扩展

在调用 mutations 中的一些函数时,可能调用的函数名会写错,可以通过定义常量的方式避免

在 store 文件夹中创建文件 mutations-type.js 文件

mutations-type.js

export const INCREMENT = 'increment'
export const DECREMENT = 'increment'

在 store 文件夹中的方法名称也可以按这样来定义

import { createStore } from 'vuex'
import {INCREMENT, DECREMENT} from './mutations-type.js'

export default createStore({
  state: {
  },
  // 此方式能减少错误
  mutations: {
    [INCREMENT](state) {
      state.count ++
    },
    [DECREMENT](state) {
      state.count --
    },
  },
  getters: {
  },
  actions: {
  },
  modules: {
  }
})

在别的地方引用时只需要写以下代码即可

import {INCREMENT, DECREMENT} from './store/mutations-type.js'
// . . . 
this.$store.commit(INCREMENT)
// . . . 
this.$store.commit(DECREMENT)
// . . . 

使用 Axios 进行网络请求

index.js

// 导入相关模块
import axios from 'axios'


axios({
  	// 该地址可以用来网络测试
  	url: 'http://httpbin.org'
    // 默认情况下为 get 请求,可以通过设置 method 的值来修改请求方式
    method: 'get'
    // 通过 params 属性来指定传入的 参数
    params: {
    	name: xiaoming,
    	age: 18
	}
   	// 由于 axios 支持 Promise 故可以使用 Promise 的方式来接收数据
}).then(res => {
  	console.log(res)
})
axios 发送并发请求
import axios from 'axios'

// 该方法返回所有请求到的结果,并将结果放入数组中,在 then 中拿到 results 该对象为一个数组
axios.all([axios({
    // 设置基地址
    baseURL: 'http://httpbin.org'
    url: '/home'
}), axios({
    url: 'http://httpbin.org'
})]).then(results => {
    console.log(results)
})

axios.all([axios({
    url: 'http://httpbin.org'
}), axios({
    url: 'http://httpbin.org'
})]).then(axios.spread((res1, res2) => {	// 通过 axios.spread 方法可以将拿到的结果进行分割
    console.log(res1)
    console.log(res2)
}))
axios 的全局配置
import axios from 'axios'

axios.default.baseURL = "http://httpbin.org"
// 设置超时时间,单位为毫秒
axios.timeOut = 50000
axios 实例
import axios from 'axios'

const instance1 = axios.create({
    baseURL: 'http://httpbin.org',
    timeout: 5000
})

const instance2 = axios.create({
    baseURL: 'http://baidu.com',
    timeout: 3000,
    headers: {
        
    }
})

instance1({
    url: '/home',
    params: {
        name: xiaoming
    }
}).then(res => {
    console.log(res)
})

instance2({
    url: '/category',
    params: {
        name: xiaoming,
        age: 18
    }
}).then(res => {
    console.log(res)
})
模块化网络请求

在设计网络请求模块时,最好将网络请求单独写到一个文件中,这样当该网络请求框架不在维护时,可以很容易更换别的框架

单独创建一个文件夹:network,在该文件夹中创建 request.js 文件

request.js

import axios from 'axios'

// 方式一:
export function request(config, success, failure) {
    const instance = axios.create({
        baseURL: 'http://baidu.com',
        timeout: 5000
    })
    
    instance(config).then(res => {
        success(res)
    }).catch(err => {
        failure(err)
    })
}

// 方式二:
export function request(config) {
    const instance = axios.create({
        baseURL: 'http://baidu.com',
        timeout: 5000
    })
    
    instance(config.baseConfig).then(res => {
        config.success(res)
    }).catch(err => {
        config.failure(err)
    })
}

// 方式三:
export function request(config) {
    return new Promise((resolve, reject) => {
        const instance = axios.create({
            baseURL: 'http://baidu.com',
            timeout: 5000
        })

        instance(config).then(res => {
            resolve(res)
        }).catch(err => {
            reject(err)
        })
    })
}

// 方式四:
export function request(config) {
    const instance = axios.create({
        baseURL: 'http://baidu.com',
        timeout: 5000
    })

    return instance(config)
}

在使用该模块时:

import { request } from './network/request.js'

// 对应方式一
request({
    url: '/home'
}, res=> {
    console.log(res)
}, err => {
    console.log(err)
})

// 对应方式二
request({
    baseConfig: {
        url: '/home'
    },
    success: function(res) {
        console.log(res)
    },
    failure: function(err) {
        console.log(err)
    }
})

// 对应方式三和四
request({
    url: '/home'
}).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})
axios 的拦截器
import axios from 'axios'

export function request(config) {
    return new Promise((resolve, reject) => {
        const instance = axios.create({
            baseURL: 'http://baidu.com',
            timeout: 5000
        })
        
        // 请求拦截器
        instance.interceptors.request.use(config => {
            // 当需要对配置信息进行修改时可以使用拦截器进行修改,在用户请求网络数据时,可以添加响应动画,用户登录时,没有携带 token 时可以进行页面跳转
            // 该函数返回配置信息
            console.log(config)
            // 当缺少返回配置信息时,将不会请求该地址成功,即信息被拦截了。但没有继续向下执行
            return config
        }, err => {
            // 该函数返回错误信息
            console.log(err)
        })
        
        // 响应拦截器
        instance.interceptors.response.use(res => {
            // 该方法传入的参数为请求后返回的结果
            console.log(res)
            // 修改过后响应信息需要将数据进行返回,否则在别的地方接收不到该信息
            return res.data
        }, err => {
            // 请求失败时会调用该函数
            console.log(err)
        })

        return instance(config)
    })
}

nce(config).then(res => {
success(res)
}).catch(err => {
failure(err)
})
}

// 方式二:
export function request(config) {
const instance = axios.create({
baseURL: ‘http://baidu.com’,
timeout: 5000
})

instance(config.baseConfig).then(res => {
    config.success(res)
}).catch(err => {
    config.failure(err)
})

}

// 方式三:
export function request(config) {
return new Promise((resolve, reject) => {
const instance = axios.create({
baseURL: ‘http://baidu.com’,
timeout: 5000
})

    instance(config).then(res => {
        resolve(res)
    }).catch(err => {
        reject(err)
    })
})

}

// 方式四:
export function request(config) {
const instance = axios.create({
baseURL: ‘http://baidu.com’,
timeout: 5000
})

return instance(config)

}


在使用该模块时:

```js
import { request } from './network/request.js'

// 对应方式一
request({
    url: '/home'
}, res=> {
    console.log(res)
}, err => {
    console.log(err)
})

// 对应方式二
request({
    baseConfig: {
        url: '/home'
    },
    success: function(res) {
        console.log(res)
    },
    failure: function(err) {
        console.log(err)
    }
})

// 对应方式三和四
request({
    url: '/home'
}).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})
axios 的拦截器
import axios from 'axios'

export function request(config) {
    return new Promise((resolve, reject) => {
        const instance = axios.create({
            baseURL: 'http://baidu.com',
            timeout: 5000
        })
        
        // 请求拦截器
        instance.interceptors.request.use(config => {
            // 当需要对配置信息进行修改时可以使用拦截器进行修改,在用户请求网络数据时,可以添加响应动画,用户登录时,没有携带 token 时可以进行页面跳转
            // 该函数返回配置信息
            console.log(config)
            // 当缺少返回配置信息时,将不会请求该地址成功,即信息被拦截了。但没有继续向下执行
            return config
        }, err => {
            // 该函数返回错误信息
            console.log(err)
        })
        
        // 响应拦截器
        instance.interceptors.response.use(res => {
            // 该方法传入的参数为请求后返回的结果
            console.log(res)
            // 修改过后响应信息需要将数据进行返回,否则在别的地方接收不到该信息
            return res.data
        }, err => {
            // 请求失败时会调用该函数
            console.log(err)
        })

        return instance(config)
    })
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值