前端笔记之Vue(五)TodoList实战&拆分store&跨域&练习代理跨域

一、TodoList 1.1安装依赖 安装相关依赖: npm install --save-dev webpack npm install --save-dev babel-loader babel-core babel-preset-es2015 npm install --save-dev vue-loader np...

一、TodoList

1.1安装依赖

安装相关依赖:

npm install --save-dev webpack
npm install --save-dev babel-loader babel-core babel-preset-es2015
npm install --save-dev vue-loader
npm install --save-dev css-loader
npm install --save-dev vue-template-compiler
npm install --save-dev stylus stylus-loader
npm install --save vue
npm install --save vuex

 

1.2配置虚拟服务器

【第一步:安装依赖】

npm install -g json-server

【第二步:json-server的服务器启动】

启动需要json文件作为支撑,新建data.json文件做服务数据:

{
    "mapList" : [
        {"id":1 ,"title" : "吃饭", "done" : false},
        {"id":2 ,"title" : "睡觉", "done" : false},
        {"id":3 ,"title" : "做饭", "done" : false},
        {"id":4 ,"title" : "扫地", "done" : false}
    ]
}
启动命令json-server命令:
json-server 数据文件路径 -s 静态化目录 -p 8080

json-server www/data/data.json -s ./www -p 8080
-s是 --static
-p是 --port

最终没有使用webpack-dev-server模拟服务器

配置webpack.config.js文件:

const path = require('path');
const {VueLoaderPlugin} = require('vue-loader');  //最新版webpack需要引入插件

module.exports = {
     //程序的入口文件
     entry: "./www/app/app.js",

     //程序的出口(打包的文件)
     output : {
        //打包文件输出的路径
        path : path.resolve(__dirname, "www/dist"),
        //打包文件的名称
        filename : "all.js",
        // publicPath:"/public" //这是对webpack-dev-server的配置,配置虚拟路径
     },
     //让webpack监听变化,自动打包
     watch : true,
     mode : "development",
     //配置webpack的模块插件
     module:{
        //关于模块的配置规则
        rules : [
            {
                //模块规则(配置 loader、解析器等选项)
                test : /.js$/,  //解析的时候匹配到的都是js文件
                include: [
                  path.resolve(__dirname, "www/app")    //翻译什么文件夹
                ],
                exclude: [
                  path.resolve(__dirname, "node_modules") //不翻译什么文件夹
                ],
                loader : "babel-loader",
                options : {
                    presets : ["es2015"]
                }
            },
            {
                test: /.vue$/,
                include: [
                  path.resolve(__dirname, "www/app")    //翻译什么文件夹
                ],
                exclude: [
                  path.resolve(__dirname, "node_modules") //不翻译什么文件夹
                ],
                loader: 'vue-loader',
                options :{
                    loaders : {
                        stylus:'vue-style-loader!css-loader!stylus-loader'
                    }
                }
            },
            {
                test:/.css$/,
                use: ['vue-style-loader','css-loader']
            }
            // {
            //     test: /.styl(us)?$/,
            //     use: [
            //         'vue-style-loader',
            //         'css-loader',
            //         'stylus-loader'
            //     ]
            // }
        ]
     },
     resolve: {
        alias: { //配置别名
          'vue$': 'vue/dist/vue.esm.js' // 用 webpack 1 时需用 'vue/dist/vue.common.js'
        }
     },
     //最新版webpack需要引入插件
     plugins : [
        new VueLoaderPlugin()
     ]
}

【第三步:访问接口】

获取所有的数据:GET

增加数据:POST

得到某一条数据:GET

删除某一条:DELETE

更新原始数据:PUT (更新条目的所有的属性)、PATH(更新条目的某一个属性)

自带分页:page(第几页)limit(每页有多少条)

http://localhost:8080/mapList          返回.json文件所有数据(数组)
http://localhost:8080/mapList/1        返回id=1的数据,采用这种路径风格id必须为小写(数组)
http://localhost:8080/mapList/?Name=lst  返回Name=lst的用户数据(数组)

分页查询:参数为 _start, _end, _limit,并可添加其它参数筛选条件,如:
http://localhost:8080/mapList?_start=6&_limit=3
http://localhost:8080/mapList?_start=3&_end=6

排序:参数为_sort, _order 
http://localhost:8080/mapList?_sort=id&_order=asc
http://localhost:8080/mapList?_sort=user,views&_order=desc,asc 

操作符:_gte, _lte, _ne, _like
_gte大于,_lte小于, _ne非, _like模糊查询,q关键词查询
http://127.0.0.1:8080/mapList/?q=吃

1.3 TodoList

查询数据

相关步骤:

created生命周期中发出一个GETALL的异步命令,去actions里面发异步请求。

App.vue

<script>
    export default {
        created(){
            this.$store.dispatch("GETALL");
        }
    }
</script>

main.js

import Vue from "vue";
import Vuex from "vuex";
import App from "./App.vue";
Vue.use(Vuex);

const store = new Vuex.Store({
    state : {
        todos : []
    },
    mutations: {
        GETALL(state,payload){
            // state.todos.push(payload)
            state.todos = payload;
        }
    },
    actions :{
        async GETALL({commit}){
            //请求数据库中的数据,然后存储到state的todos中
            var data = await fetch("/maplist").then(data=>data.json());
            //发出commit()命令,并且将参数当载荷传递过去
            commit("GETALL", data);
        }
    }
});

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

异步请的数据回调成功后,将数据以载荷的形式发送到mutations,再去修改state 的数据

state全局仓库有了数据,就可以在App.vue进行循环渲染。

App.vue

<template>
    <div>
        <ul>
            <li v-for="item in todos">
                {{item.title}}
            </li>
        </ul>
    </div>
</template>
<script>
    export default {
        created(){
            this.$store.dispatch("GETALL");
        },
        computed : {
            todos(){
                return this.$store.state.todos
            }
        }
    }
</script>

下一步:需要将li标签拆分成组件:

App.vue

<template>
    <div>
        <ul>
           <li is="TodoLi" v-for="item in todos" :item="item"></li>
        </ul>
    </div>
</template>
<script>
    import TodoLi from "./components/TodoLi.vue";
    export default {
        created(){
            this.$store.dispatch("GETALL");
        },
        computed : {
            todos(){
                return this.$store.state.todos
            }
        },
        components:{
            TodoLi
        }
    }
</script>

TodoLi.vue

<template>
     <li>{{item.title}}</li>
</template>
<script>
    export default {
        props : ["item"]
    }
</script>

删除数据

接下来写增删改查业务,推荐先写删除,因为删除值需要id

TodoLi.vue

<template>
     <li>
         <span>{{item.title}}</span>
         <button @click="del(item.id)">删除</button>
    </li>
</template>
<script>
    export default {
        props : ["item"],
        methods:{
            del(id){
                this.$store.dispatch("DEL", {id})
            }
        }
    }
</script>

const store = new Vuex.Store({
    state : {
        todos : []
    },
    mutations: {
        GETALL(state,payload){
            // state.todos.push(payload)
            state.todos = payload;
        },
        DEL(state, payload){
            // 根据id删除state的数据
            state.todos = state.todos.filter(item=>{
                return item.id != payload.id;
            })
        }
    },
    actions :{
        async GETALL({commit}){
            //请求数据库中的数据,然后存储到state的todos中
            var data = await fetch("/maplist").then(data=>data.json());
            //发出commit()命令,并且将参数当载荷传递过去
            commit("GETALL", data);
        },
        async DEL({commit},payload){
            //向服务器发出删除请求,删除data.json中的数据
            var data = await fetch("/maplist/" + payload.id, {
                method:"DELETE",
            }).then(data => data.json());
            commit("DEL", payload); //删除全局state仓库的数据
        },
    }
});

新增业务

App.vue

<template>
    <div>
        <div>
            <input type="text" v-model.trim="txt">
            <button @click="add">新增</button>
        </div>
        <ul>
           <li is="TodoLi" v-for="item in todos" :item="item"></li>
        </ul>
    </div>
</template>
<script>
    import TodoLi from "./components/TodoLi.vue";
    export default {
        data(){
            return {
                txt : ''
            }
        },
        methods :{
            add(){
                if(this.txt == '') return;
                //发出新增的异步请求
                this.$store.dispatch(ADD, {id: new Date() - 0, title:this.txt, done:false})
            this.txt = ""
            }
        },
        components:{
            TodoLi
        }
    }
</script>

main.js

actions : {
    //新增数据
    async ADD({ commit }, payload) {
        //向data.json文件中插入数据
        var data = await fetch("/maplist/", {
            method: "POST",
            headers:{'Content-type' : 'application/json'},
            body: JSON.stringify(payload)
        }).then(data => data.json());
        commit("ADD", data); //新增数据到state,影响视图更新
    }
},
mutations : {
    ADD(state, payload) {
        state.todos.push(payload);
    }
}

 

修改数据

TodoLi.vue

<template>
     <li>
         <span v-if="!isShow" @dblclick="showInput">{{item.title}}</span>
         <input type="text" v-if="isShow" v-model="item.title" @blur="hideInput(item)" v-focus>
         <button @click="del(item.id)">删除</button>
    </li>
</template>
<script>
    export default {
        props : ["item"],
        data(){
            return{
                isShow:false
            }
        },
        methods:{
            del(id){
                this.$store.dispatch("DEL", {id})
            },
            showInput(){
                //双击显示input
                this.isShow = !this.isShow;
            },
            hideInput(item){
                console.log(item)
                //失去焦点隐藏input,并且发送CAHNGETITLE命令修改title内容
                this.isShow = !this.isShow;
                this.$store.dispatch("CHANGETITLE",item)
            }
        },
        directives:{
            //自定义组件指令,自动获取焦点
            focus :{
                inserted(el){
                    el.focus();
                }
            }
        }
    }
</script>

main.js

const store = new Vuex.Store({
    state : {
        todos : []
    },
    mutations: {
        CHANGETITLE(state, payload) {
            // 写法1
            // state.todos.forEach(item=>{
            //     if (item.id == payload.id){
            //         item.title = payload.title;
            //     }
            // })
            // 写法2
            state.todos = state.todos.map(item => {
                if(item.id == payload.id) {
                    return payload
                }
                return item;
            })
        }
    },
    actions :{
        //修改请求
        async CHANGETITLE({commit}, payload) {
            var data = await fetch("/maplist/" + payload.id, {
                method: "PATCH",
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(payload)
            }).then(data => data.json());

            commit("CHANGETITLE", data); //虽然实现了修改,但是为了完整性还是写上
        },
    }
});

修改任务事件:当前任务是否做了,需要一个复选框

todoli.vue

<li>
    <input type="checkbox" v-model="item.done" ref="cbox" @click="changeDone(item)">
<spanv-if="!isShowInput"@dblclick="showInput" :class="{cur:item.done}">{{item.title}}</span>
</li>
<script>
methods:{
changeDone(item){
       console.log(this.$refs.cbox.checked);
       // 这里使用的是ref钩子 这样能得到 复选框的状态 
       // 提交的时候 只需要提交 id  复选框的状态就行了
       this.$store.dispatch("CHANGEDONE",{
          id : item.id,
          done : this.$refs.cbox.checked
       })
}
}
</script>
<style>
    .cur{text-decoration: line-through;}
</style>

app.js修改done和修改title的方式方法都一样,只要改方法名即可。

const store = new Vuex.Store({
    mutations:{
        CHANGEDONE(state, payload){
            state.todos = state.todos.map(item => {
                if(item.id == payload.id){
                    return payload
                }
                return item
            })
        }
    },
actions:{
        async CHANGEDONE({commit},payload){
            var data = await fetch("/mapList/" + payload.id,{
                "method" : 'PATCH',
                "headers":{
                    'Content-Type':"application/json"
                },
                body:JSON.stringify({done:payload.done})
            }).then(res => res.json()); 
            commit("CHANGEDONE",data)
        },
    }
});

筛选按钮:计算已做未做事项

筛选按钮:计算已做未做事项

app.js

const store = new Vuex.Store({
    state:{
        todos:[]
    },
    mutations:{
        ...
    },
    actions:{
        ...
    },
    getters:{
        yizuo:function(state){
            return state.todos.filter(item => {
                return item.done == true //true表示已做
            })
        },
        weizuo:function(state){
            return state.todos.filter(item => {
                return item.done == false //false表示未做
            })
        }
    }
});

App.vue

<template>
    <div>
        <div>
            <input type="text" v-model.trim="txt">
            <button @click="add">新增</button>
        </div>
        <ul>
            <li v-for="item of todos" is="TodoLi" :item="item"></li>
        </ul>
        <div>
                全部:{{$store.state.todos.length}}个事项<br>
                已做:{{$store.getters.yizuo.length}}个完成事项<br>
                未做:{{$store.getters.weizuo.length}}个代办事项<br>
        </div>
        <div>
            <button @click="all">查看全部</button>
            <button @click="yizuo">查看已做</button>
            <button @click="weizuo">查看未做</button>
        </div>
    </div>
</template>
<script>
import todoli from './components/TodoLi.vue'
export default {
    data() {
        return {
            txt: '',
            state: 'all'
        }
    },
    computed: {
        todos() {
            if(this.state == 'all'){
                return this.$store.state.todos
            }else if(this.state == 'yizuo'){
                return this.$store.getters.yizuo
            }else if(this.state == 'weizuo'){
                return this.$store.getters.weizuo
            }

        }
    },
    methods: {
        all() {
            this.state = 'all'
        },
        yizuo() {
            this.state = 'yizuo'
        },
        weizuo() {
            this.state = 'weizuo'
        }
    }
}
</script>

所有业务都已经完成,但是由于组件和数据太乱,下面开始拆分store


二、拆分store

我们都知道vuexvue用来集中管理状态的容器(管理全局的状态),实现不同组件之间相互的数据访问。我们说一下vuex拆分store以及多模块管理。如果一个项目非常大的话状态就会非常的多,如果不进行分类处理,所有的状态都维护在一个state里面的话,状态管理就会变得非常的混乱,这样非常不利于项目的后期维护。现在前端推崇模块化开发,为的就是提高开发效率和维护效率,避免重复工作。那么vuex是怎么样解决这个问题的呢?这个时候我们今天要讲的主角modules就要闪亮登场了。 

第一步:将main.js中的store拆分到一个js文件中。

export const storeObj = {
    state:{
        ...
    },
    mutations:{
        ...
    },
    actions:{
       ...
    },
    getters:{
        ...
    }
}

store中的index.jsmain.js中引入

import {storeObj} from "./store/index.js";
const store = new Vuex.Store(storeObj);

第二步:拆分statemutationsactionsgetters

给他们分别创建单独的js文件,默认暴露

export default {

}

可以将我们定义的这些对象加入到Vuexstore

storeindex.jsimport引入:

import state from "./state.js";
import mutations from "./mutations.js";
import actions from "./actions.js";
import getters from "./getters.js";

export const storeObj = {
    state:{
        ...state
    },
    mutations:{
        ...mutations
    },
    actions:{
        ...actions
    },
    getters:{
        ...getters
    }
}
示例代码

由于没装翻译...”对象解构语法,会报错:

npm install --svae-dev babel-plugin-transform-object-rest-spread

配置webpack.config.js

{
    test: /.js?$/,  //解析的时候匹配到的都是js
    loader: "babel-loader",
    //翻译字典
    options: {
        presets: ["es2015","ES2015"],
        plugins: ["transform-object-rest-spread"]
    }
},

配置完成后就可以识别...”解构了。

至此,还没拆完,使用常量代替mutations集合actionstype名字

新建一个types.js文件,在里面存常量(大写字母的方法)

注意:把这些常量放到单独的文件中可以让你的代码合作者对整个app包含mutationactions一目了然。

用不用常量代替取决于你--在需要多人协作的大型项目中,这会很有帮助,但是如果你不喜欢,完全可以不这么做。

export const GETALL = "GETALL";
export const DEL = "DEL";
export const ADD = "ADD";
export const CHANGETITLE = "CHANGETITLE";
export const CHANGEDONE = "CHANGEDONE";

暴露常量的名字后,分别在mutationsactionsjs文件中使用这些来代替:

mutations.js

import {GETALL,DEL,ADD,CHANGETITLE,CHANGEDONE} from "./types.js";
export default {
    [GETALL](state, payload) {
        
    },
    [DEL](state, payload) {
        
    },
    [ADD](state, payload) {
        
    },
    [CHANGETITLE](state, payload) {
        
    },
    [CHANGEDONE](state, payload) {
        
    }
}

actions.js

import {GETALL,DEL,ADD,CHANGETITLE,CHANGEDONE} from "./types.js";
export default {
    async [GETALL]({ commit }) {
        
    },
    async [DEL]({ commit }, payload) {
        
    },
    async [ADD]({ commit }, payload) {
        
    },
    async [CHANGETITLE]({ commit }, payload) {
        
    },
    async [CHANGEDONE]({ commit }, payload) {
        
    }
}

注意:上方mutationsactions中的[types.ADD]写法,因为types.ADD是一个对象,所以不能直接当做函数名来写,需要用到ES2015风格的计算属性命名功能来使用一个常量作为函数名,才能使用。

https://vuex.vuejs.org/zh/guide/mutations.html


三、异步请求数据(跨域)

3.1 Ajax不能跨域

比如在Apache中演示:

<html>
<head>
    <title>Document</title>
</head>
<body>
    <script src="jquery-3.3.1.min.js"></script>
    <script>
        $.get("http://127.0.0.88/a.txt" , function(data){
            console.log(data);
        });
    </script>
</body>
</html>

试图在127.0.0.1请求127.0.0.88/a.txt

  • 发表于 2019-05-19 22:40
  • 阅读 ( 176 )
  • 分类:网络文章

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除