zoukankan      html  css  js  c++  java
  • 前端笔记之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
    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.txtJSON数据,会报错:

     

    浏览器会阻止对ip地址不同,或者端口号不同的数据请求。

    此时我们必须要进行请求,就是跨域技术。


    3.2jQuery复习JSONP跨域

    <html>
    <head>
        <title>Document</title>
    </head>
    <body>
        <script>
            function haha(info){
                console.log(info);
            }
        </script>
        <script src="http://127.0.0.88/a.txt"></script>
    </body>
    </html>

    在页面上定义一个函数,然后用script标签引入这个语句,执行这个语句。此时数据就通过函数的实参和形参的结合,传入了页面。script没有跨域限制的,可以轻松src引入其他ip地址的js文件。

    JSONP叫做JSON Padding(补白)

    我们写一个接口,这个接口不是一个JSON,而是一个语句,这个语句是函数的调用

     

    升级:把a.txt改为a.php(这个文件不需要会,因为是后端的事情)

    <?php
    $callback = $_GET["callback"];    //获取get请求
    echo $callback . '({"a" : 100 })';//JSON参数
    ?>

    点击按钮再发出JSONP跨域请求。

    本质上是点击按钮的瞬间,创建了一个script标签,script标签的src指向了我们刚刚写的那个接口,那个接口是函数的调用。我们快速定义这个函数,使得src引入的js能够调用这个函数,从而通过实参和形参的结合,将数据传入页面。

    <script>
        $("button").click(function(){
             //创建script标签
             var $o = $("<script></script>");
             //创建一个src
             $o.attr("src" , "http://127.0.0.88/a.php?callback=xixi");
             //定义全局函数
             window.xixi= function(data){
                 //接收到数据的回调函数
                 console.log(data);
             }
             //上树
             $("head").append($o); 
             //下树
             $o.remove();
         });
    </script>

    但实际上不用这么麻烦,jQuery会自动帮我们发出jsonp跨域:

    <html lang="en">
    <head>
        <title>Document</title>
    </head>
    <body>
        <button>按我发出请求</button>
        <script>
            $("button").click(function(){
                $.ajax({
                    "dataType" : "JSONP" ,
                    "url" : "http://127.0.0.88/a.php",
                    "success" : function(data){
                        console.log(data);
                    }
                })
            });
        </script>
    </body>
    </html>

    jQuery会随机生成一个函数名。

     

    原理和我们写的一样的,都是:

    1)瞬间创建一个内部函数

    2)瞬间创建一个script标签,src指向我们的url地址

    3)瞬间上树

    4)瞬间下树


    3.3跨域方法1JSONP跨域

    之前我们项目的数据和前端的文件都是同源的(就是在同一个接口下)。

    但是,工作中,一定是要跨域请求数据的。

    比如我们的前端项目运行在8080端口,要请求3000端口的数据,就是跨域。

    事实上,工作中,模拟接口一定是不同ip的,模拟接口运行在办公室的局域网上的。

    每个人的电脑都是同一个局域网的,后台哥哥会给你一个后端接口文档,接口在演示的时候,很有可能是30008000等等。但你的前端项目跑在webpack-dev-server8080端口,就涉及到跨域请求数据了。

    创建一个后端文件夹(server_side)和前端项目文件夹(Vue_study):

     

    前端后端共同跑:后端3000,前端8080

     

    app.js

    var express = require("express");
    var app = express();
    
    app.get("/a", function(req,res){
        // res.json({"a":100});
        res.jsonp({"a":100}); //jsonp()能自动得到callback参数请求
    })
    
    app.listen(3000);
    示例代码

    http://127.0.0.1:3000/a?callback=haha

     

    App.vue

    <template>
        <div>
            <h1>首页</h1>
            <button @click="addServer">请求数据</button>
        </div>
    </template>
    <script>
    export default {
        methods:{
            addServer(){
                $.ajax({
                    dataType:"JSONP",
                    url: "http://127.0.0.1:3000/a",
                    success:function(data){
                        console.log(data)
                    }
                })
            }
        }
    };
    </script>
    示例代码

     

    3.4跨域方法2CORS跨域

    面试想要回答的好,看下面文章:

    https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

    http://www.ruanyifeng.com/blog/2016/04/cors.html

    跨域资源共享(CORSCross-Origin Resource Sharing)机制允许Web应用服务器进行跨域访问控制,从而使得跨域数据传输得以安全进行。浏览器支持在API容器中(例如XMLHttpRequestfetch)使用CORS,以降低跨域HTTP请求所带来的风险。

    说白了,就是在HTTP下响应的头部加一句话。

    CORS是一个W3C标准,全称是"跨域资源共享"Cross-origin resource sharing)。

    它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制

    我们在后端一般就加Access-Control-Allow-Origin 值为*

     

    问题:8080的前端页面请求3000后端的页面,是谁禁止的?是浏览器禁止的,为了安全。此时就要写一个HTTP下行响应头部,来告诉浏览器,这次请求是允许的,这是最简单的跨域方法,但是IE8才开始兼容。

     

    app.js后端:

    var express = require("express");
    var app = express();
    
    app.get("/a", function(req,res){
        //表示允许任何域名来源的请求
        res.setHeader("Access-Control-Allow-Origin", "*");
        res.json({a:100});
    })
    
    app.listen(3000);

    App.vue此时在前端项目中就可以随意跨域:

    <template>
        <div>
            <h1>首页</h1>
            <button @click="addServer">请求数据</button>
        </div>
    </template>
    <script>
    export default {
        methods:{
            addServer(){
               $.get("http://127.0.0.1:3000/a", function(data){
                    console.log(data)
               })
            }
        }
    }
    </script>
    示例代码

    3.5跨域请求方法3:代理跨越(proxy

    “代理”就是偷数据。

    PHP做例子,实现一下偷百度首页(了解即可)

    <?php
        echo file_get_contents("http://www.baidu.com/");
    ?>

    运行tou.php就直接是百度首页了。

     

    比如京东充话费接口,可以被后端偷来:

     

    然后在前端项目中使用:

    <body>
        <h1>手机归属地查询(业绩最快,完全免费)</h1>
        <input type="text" id="tel">
        <button>查询</button>
    </body>
    <script type="text/javascript" src="js/jquery-2.2.4.min.js"></script>
    <script type="text/javascript">
        $("button").click(function(){
            var tel = $("#tel").val();
            //可以请求自己服务器的数据,只不过数据由后端tou.php偷回来。
            $.get("http://127.0.0.1:8088/tou.php?tel=" + tel, function(data){
                console.log(data)
            })
        });
    </script>
    示例代码


    【重点】:如何在VueReact项目中使用代理跨域?

    webpack-dev-server可以帮我们偷服务端的数据,美其名曰“代理”。

    相当于服务器去“偷数据”,由后端(前端的后端,就是webpack-dev-server)出面,问3000端口要数据

    webpack.config.js中,加几句话即可实现“代理跨域”。

    https://webpack.docschina.org/configuration/dev-server/#devserver-proxy

    webpack-dev-server这个小型服务器底层也是express实现的

    webpack-dev-server在帮我们做几件事:

    1)构建app文件夹到/public/all.js

    2)静态化www文件夹提供了静态文件的路由

    3)代理了127.0.0.1:3000的接口。

    4)构建app的时候进行了翻译

    webpack.config.js配置:

    var path = require('path');
    const {VueLoaderPlugin} =  require("vue-loader"); //最新版webpack需要引入此插件
    
    module.exports = {
        //程序的入口文件
        entry: './www/app/app.js',
        //程序的出口文件(被打包的文件)
        output: {
          ...
        },
        //自动监听
        watch:true,
        mode : "development",
        //配置webpack的模块插件
        module:{
            // 关于模块配置
            ...
        },
        resolve: {
            ...
        },
        devServer: {
            proxy: {
                '/api': {
                    target: 'http://127.0.0.1:3000', //设置你调用的接口域名和端口
                    //这里理解成/api代理target中的地址,后面组件中调用接口要使用/api代替
                    pathRewrite: {'^/api' : ''}
                }
            }
        },
        //最新版webpack需要引入此插件
        plugins:[
           new VueLoaderPlugin()
        ]
    };

    这些配置都从webpack官网抄的,配置就可以做项目了,实现接口的请求。

    配置好后,重启 npm run dev比如3000有一个接口/a,现在将被代理到8080接口的/api/a

    http://127.0.0.1:3000/a这个接口,被“代理”到http://127.0.0.1:8080/api/a 

     

     

    如果后端app.js出一个这样的接口:http://127.0.0.1/:3000/a

    app.get("/a",function(req,res){
        res.json({"a":100});
     });

    前端app.js就可以这样获取后端的接口:http://127.0.0.1/:8080/api/a

    <template>
        <div>
            <h1>首页</h1>
            <button @click="addServer">请求数据</button>
        </div>
    </template>
    <script>
    export default {
        methods : {
            addServer(){
                $.get("http://127.0.0.1:8080/api/a",function(data){
                    console.log(data);
                })
            }
        }
    }
    </script>

    总结:一提起“跨域”,立马回答他一共有三种方法。


    四、练习代理跨域

    后端app.js提供数据接口:(node app.js运行)

    var express = require("express");
    var app = express();
    app.get("/a" , function(req,res){
        res.json({"a":100});
    });
    app.listen(3000);
    示例代码

    前端main.js

    import Vue from "vue";
    import Vuex from "vuex";
    import App from "./App.vue";
    import store from "./store";
    
    new Vue({
        el:"#app",
        store,
        render:(h)=>h(App)
    })

    前端App.vue:

    <template>
        <div>
            <h1>{{$store.state.counterState.a}}</h1>
            <button @click="add(1)">加1</button>
            <button @click="add(2)">加2</button>
          <button @click="addServer">按我加服务器的数据</button>
        </div>
    </template>
    <script>
        export default {
            methods : {
                add(n){
                    this.$store.commit("ADD", {n});
                },
                addServer(){
                    //异步必须用dispatch
                    this.$store.dispatch("ADDSERVER");
                }
            }
        }
    </script>

    store中的index.js

    import Vue from "vue";
    import Vuex from "vuex";
    import createLogger from 'vuex/dist/logger';
    
    import counterState from "./counter/state.js";
    import counterMutations from "./counter/mutations.js";
    import counterActions from "./counter/actions.js";
    
    Vue.use(Vuex);
    //全局数据
    export default new Vuex.Store({
        state: {
            counterState
        },
        //同步的(commit)
        mutations: {
            ...counterMutations
        },
        //异步的(dispatch)
        actions : {
            ...counterActions
        },
        plugins: [createLogger()]
    });

    state.js

    export default {
        a: 100
    }
    实例代码

    mutations.js

    export default {
        ADD(state, payload) {
            state.counterState.a += payload.n;
        } 
    }

    actions.js

    export default {
        async ADDSERVER({commit}){
            //发出异步请求
            const {a} = await fetch("/api/a").then(data=>data.json());
            //异步函数不能改变state,改变state只有mutations
            commit("ADD" , {n:a}) //触发mutation
      }
    }

  • 相关阅读:
    UVALive 7141 BombX
    CodeForces 722D Generating Sets
    CodeForces 722C Destroying Array
    CodeForces 721D Maxim and Array
    CodeForces 721C Journey
    CodeForces 415D Mashmokh and ACM
    CodeForces 718C Sasha and Array
    CodeForces 635C XOR Equation
    CodeForces 631D Messenger
    田忌赛马问题
  • 原文地址:https://www.cnblogs.com/rope/p/10737428.html
Copyright © 2011-2022 走看看