zoukankan      html  css  js  c++  java
  • 利用vue-cli配合vue-router搭建一个完整的spa流程

    好文章备忘录:

     转自:https://segmentfault.com/a/1190000009160934?_ea=1849098



    demo源码:https://github.com/1590123375...

    demo未安装依赖,下载完成,npm install后再npm run dev运行。

    利用vue-cli配合vue-router搭建一个完整的spa流程(一)

    前言
    Ⅰ. demo所用vue-router的一些基本操作。vue-router中文文档,快速浏览一遍即可 http://router.vuejs.org/zh-cn/
    Ⅱ. 整个demo所用到的技术栈 vueJS(2.0) vue-cli vue-resource es6
    Ⅲ. 所需构建工具 nodeJS Git

    第一步:安装

    OK,正题开始,首先保证nodeJS,Git,webpack已安装完毕。打开项目文件夹,安装vue-cli。


    全局安装 vue-cli
    $ npm install --global vue-cli

    创建一个基于webpack的模板
    vue init webpack my-project
    创建过程中,vue-router为必须,其他语法检测,单元测试等按需求安装。

    创建完成后进入项目文件夹,安装依赖
    $ npm install

    安装到此结束,运行如下代码,显示为图片所示,则安装成功。
    $ npm run dev


    第二步:项目文件及运行流程

    Ⅰ: 项目文件

    打开已经创建好的模板

    如图所示,只会用到,src,static,index.html这三个文件。首先解释一下三个文件的作用:
    Ⅰ: src 存放路由JS,模板.vue文件,入口JS,以及一个入口.vue文件
    Ⅱ: static 存放静态文件
    Ⅲ: index 入口html文件

    这里解释一下xxx.vue文件是什么,官网叫其为单文件组件,通过webpack源码转换,会全部转换为对应的文件。
    说白了就是一个包裹,里边含有三部分 一部分模板template,一部分样式style,一部分JSjavascript,他们封装在一起。
    如下图所示:

    Ⅱ: 运行流程

    写起来比较麻烦,做了一张图,直截了当。

    第三步:搭建基本路由框架

    项目文件明了之后,我们开始搭建一个简单的SPA路由构架:
    Ⅰ: 页面中有俩个及俩个以上的分类
    Ⅱ: 每个分类中可以点击进入到详情页面
    Ⅲ: URL输入错误后展示404页面
    Ⅳ: 在页面中刷新,根据URL重新获取数据,渲染页面

    根据基础框架创建对应的文件。

    文件详解:
    Ⅰ: src中components文件夹里新建三个xxx.vue文件,
    ①error.vue 此为404页面
    ②showone.vue 此为第一个分类页面
    ③showtwo.vue 此为第二个分类页面

    Ⅱ: src中zjapp.vue这是路由入口文件

    Ⅲ: static中img为详情页面大图,thumbnail为分类页面缩略图

    Ⅳ: 俩个JSON文件,分别代表分类一和分类二的数据来源

    Ⅴ: bootstrap.css 样式CSS

    到此路由的基本框架搭建完成,后面开始代码的填充。

    第四步:主页面代码编写

    Ⅰ: index.html

    作为页面入口文件,先引入Bootstrap.CSS,如果是本地文件放在static文件夹里。可以使用CDN或者npm安装。
    为了方便后面阅读将id="app"更改为id="index"。当然,也可以不更改,main.js中有多个为app的名字,避免混淆。

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>spa-vue-demo</title>
        <link rel="stylesheet" href="static/bootstrap.min.css" />
      </head>
      <body>
        <div id="index"></div>
        <!-- built files will be auto injected -->
        </body>
    </html>

    Ⅱ: main.js

    main.js为入口JS文件,Vue的实例在这里书写。el 挂载在 index.html 中 id="index" 的标签上。

    import Vue from 'vue'
    import App from './zjapp'
    import router from './router'
    
    Vue.config.productionTip = false
    
    new Vue({
      el: '#index',
      router,
      template: '<App/>',
      components: { App }
    })

    Vue 开始渲染时,加载 components: { App } 组件替换生成在 id="index" 内的 <App></App> 标签,那么{ App }来自哪里呢?

    答案在 import App from './zjapp' 这里是ES6语法,引入zjapp.vue模块中暴露出来的接口,后缀可以不写。

    Vue 实例中的 router 属性也是ES6中对象的字面量写法,等于router:router。同理 import router from './router' 这里引入router。

    因为,router中index.js暴露接口时没有署名,这里也可以改一个名字,比如:

    import Vue from 'vue'
    import App from './zjapp'
    
    //修改名字一样可以。
    import changeES6 from './router'
    
    Vue.config.productionTip = false
    
    new Vue({
      el: '#index',
    
      //修改在这里
      router:changeES6,
    
      template: '<App/>',
      components: { App }
    })

    最后,可能有人会问 Vue.config.productionTip = false 是做什么用的,其实这里是关闭了生产模式即部署到服务器后给出的提示。

    Ⅲ: zjapp.vue

    这个文件是 Vue 一开始渲染组件时的文件,首先贴出全部代码,很多,但是会全部讲解作用,含义。

    <template>
        <div class="container">
            <div class="row">
                <div id="index" class="col-xs-12 col-lg-12 col-md-12" style="padding: 0;">
                    <transition name='animate' appear mode='out-in'>
                        <router-view v-bind:router-data="allData" v-bind:key="change"></router-view>
                    </transition>
                    <transition name='btn' appear mode='out-in'>
                        <div class="app-btn" v-show="allData.mainShow">
                            <button class="btn btn-success app-btn-back" v-show="back==0?false:true" v-bind:key="back" v-on:click="dosom('back')">上一页</button>
                            <button class="btn btn-success app-btn-next" v-show="next==0?false:true" v-bind:key="next" v-on:click="dosom('next')">下一页</button>
                        </div>
                    </transition>
                    <div class="app-loading" v-show="loading">
                        <img src="../static/loading/loading.gif" style="margin:0 auto;display: block;" alt="loading" />
                    </div>
                </div>
            </div>
        </div>
    </template>
    <script>
        import router from './router'
        export default{
            data(){ 
                return{
                    allData:{
                        showData:null,
                        detailedData:{},
                        num:0,
                        mainShow:true
                    },
                    loading:false,
                    change:true,
                    back:0,
                    next:1,
                }
            },
            created(){
                this.routePath();
            },
            watch:{
                "$route"(to){
                    this.routePath();
                }
            },
            methods:{
                buttonToggle(){
                    var nowNum=this.allData.num;
                    this.back=nowNum;
                    this.next=2-nowNum;
                },
                dosom(str){
                    str=="next"?this.allData.num++:this.allData.num--;
                    this.buttonToggle();
                    //当前user/当前页面/当前页面路由
                    router.push(this.$route.path.slice(0,8)+this.allData.num);
                },
                routePath(){
                    if(this.$route.fullPath=="/"){
                        router.push("/user/0/0");
                        this.load();
                    }
                    else if(this.$route.fullPath.length==9 || this.$route.fullPath.length==20){
                        this.load();
                    }
                    else{
                        router.push("/user/error");
                        this.back=0;
                        this.next=0;
                    }
                },
                load(){
                    var numData=null,
                        listData=null;
                        
                    // /user/lisData/numData
                    numData=this.$route.path.slice(8,9);
                    listData=this.$route.path.slice(6,7);
                    
                    // 初始化
                    this.allData.num=numData;
                    this.buttonToggle();
                    this.change=!this.change;
                    if(this.$route.path.indexOf("con")>0){
                        //获取list中第几个
                        var typeData=this.$route.query.type;
                        this.$nextTick(e=>{
                            this.$http.get("static/data-"+listData+".json").then(rea=>{
                                this.loading=true;
                                setTimeout(e=>{
                                    
                                    //vue-resource加载数据存在于data.body中
                                    var listNum=rea.body.allData.slice(numData*6,numData*6+6);
                                    
                                    //详细显示页面数据来源
                                    this.allData.detailedData=listNum.slice(typeData,typeData+1)[0];
                                    
                                    this.loading=false;
                                },700);
                            });
                                
                        });
                        this.allData.mainShow=false;
                    }else{
                        this.$nextTick(e=>{
                            this.loading=true;
                            setTimeout(e=>{
                                this.$http.get("static/data-"+listData+".json").then(rea=>{
                                    
                                    this.allData.showData=rea.body.allData.slice(numData*6,numData*6+6);
                                    this.loading=false;
                                });
                            },700);
                        });
                        this.allData.mainShow=true;
                    }
                }
            }
        }
    </script>
    <style>
        /*切换中动画*/
        .animate-enter-active,.animate-leave-active{
            transition: all 0.5s ease;
        }
        .animate-enter{
            transform: translateX(-80px);
            opacity: 0;
        }
        .animate-leave-active{
            transform: translateX(80px);
            opacity: 0;
        }
        /*底部按钮简单动画*/
        .btn-enter-active,.btn-leave-active{
            transition: all 1s ease;
        }
        .btn-enter{
            opacity: 0;
        }
        .btn-leave-active{
            opacity: 0;
        }
        
        
        /*back,next btn-class*/
        .app-btn{
            overflow: hidden; 
             140px;
            height: 34px;
            position: relative;
            margin-top: 15px;
        }
        /*back btn-class*/
        .app-btn-back{
            position: absolute;
            top: 0;
            left: 0;
        }
        /*next btn-class*/
        .app-btn-next{
            position: absolute;
            bottom: 0;
            right: 0;
        }
        
        
        /*loading*/
        .app-loading{
            background-color: tan;
            position: fixed;
            height: 100%;
             100%;
            left: 0;
            top:0;
        }
    </style>

    Ⅰ: HTML部分(即template)

    大体分为三部个分

    第一部分:

     <transition name='animate' appear mode='out-in'>
          <router-view v-bind:router-data="allData" v-bind:key="change"></router-view>
     </transition>

    第一部分为页面中内容路由入口,其中:
    v-bind:router-data="allData" 是对模板中传输数据用的
    v-bind:key="change" 是页面切换动画绑定的变值,用来使页面被复用时触发切换动画

    第二部分:

    <transition name='btn' appear mode='out-in'>
        <div class="app-btn" v-show="allData.mainShow">
            <button class="btn btn-success app-btn-back" v-show="back==0?false:true" v-on:click="dosom('back')">上一页</button>
            <button class="btn btn-success app-btn-next" v-show="next==0?false:true" v-on:click="dosom('next')">下一页</button>
         </div>
    </transition>

    第二部分为页面中上一页,下一页按钮部分,其中:
    v-show="allData.mainShow" 是控制俩个按钮显示,隐藏(详情页隐藏)
    v-show="back==0?false:true" 是控制单个按钮显示,隐藏(最后一页时,下一页按钮隐藏)
    v-on:click="dosom('back')" 是绑定的点击事件

    第三部分:

    <div class="app-loading" v-show="loading">
       <img src="../static/loading/loading.gif" style="margin:0 auto;display: block;" alt="loading" />
    </div>

    第三部分为页面中内容更新数据时loading画面,其中:
    v-show="loading" 是用来显示,隐藏 loading 动画

    好了,到此主要的HTML模块已经布局完毕,现在开始JS功能的开发

    Ⅱ: javascript

    JS这里的整体流程:watch router.path的变化,从URL中读取数据,从新获取数据。(因为本地JSON文件,获取JSON后对JSON进行剪切)

    import router from './router' ES6语法,引入router模块下暴露的接口,这里引入router实例为后续编写编程式导航铺垫。

    data(){ 
        return{
            allData:{
                showData:null,
                detailedData:{},
                num:0,
                mainShow:true
            },
            loading:false,
            change:true,
            back:0,
            next:1,
        }
    }

    showData 为当前页面渲染数据,即router-view被替换为showone.vue模板中的数据来源,每次点击下一页等操作导致router.path变化时,此数据更新对应的子模板中(showone.vue)数据也更新。(后面讲到路由页面时会解释)

    detailedData 为详情页面渲染数据,即router-view被替换为showtwo.vue模板中的数据来源,同上。

    num 这个是用来每次打开或者刷新页面时读取当前为第几页的number,因为这个数值用了很多次,故将它放到了初始化函数里

    mainShow 控制俩个按钮(下一页,上一页)总体显示,隐藏

    loading 控制loading动画的显示,隐藏

    change 页面复用时的Key值

    back 返回按钮的number,因为按钮的判断为v-show="back==0?false:true"当为0是隐藏

    next 同上


    以上为这个demo中数据的含义,下面是方法的解释,从methods开始说起:

    buttonToggle(){
        var nowNum=this.allData.num;
        this.back=nowNum;
        this.next=2-nowNum;
    }

    这是俩个按钮的控制函数,因为JSON数据不多,一个分类中只有2页数据,所以 this.next=2-nowNum; 最后一页时隐藏。

    dosom(str){
        str=="next"?this.allData.num++:this.allData.num--;
        this.buttonToggle();
    
    //  http://localhost:8080/#/user/0/1
    //  http://localhost:8080/#/user/0/this.allData.num
        router.push(this.$route.path.slice(0,8)+this.allData.num);
    }

    这是按钮点击时触发的方法,点击后判断是上一页,还是下一页,因为会动态的隐藏按钮所以不用关注++或者--的上下界。随后进行url的更改(url更改后会触发watch,watch中执行的函数为 routePath() ,下一个说到)

    routePath(){
        if(this.$route.fullPath=="/"){
            router.push("/user/0/0");
            this.load();
        }
        else if(this.$route.fullPath.length==9 || this.$route.fullPath.length==20){
            this.load();
        }
        else{
            router.push("/user/error");
            this.back=0;
            this.next=0;
        }
    }

    this.$route.fullPath 返回的是全部 url 字符串,这是当前url判断函数:

    当读取到的url为“/”时,此为第一次打开页面,跳转到首页也就是 http://localhost:8080/#/user/0/0 然后 执行load()方法,load() 方法下一个说到。

    当读取到this.$route.fullPath.length==9 || this.$route.fullPath.length==20,其实就是 this.$route.fullPath 为/user/x/x 的主页面中,或者为 /user/x/x/con?type=x 的详情页面中,此时直接进行 load() 方法更新数据即可

    最后其他任何 url 都被认为是错误的 http 请求,返回404页面,当然俩个翻页按钮隐藏。

    load(){
        var numData=null,
        listData=null;    
            
    //  /user/lisData/numData
        listData=this.$route.path.slice(6,7);
        numData=this.$route.path.slice(8,9);    
            
    //  初始化num值
        this.allData.num=numData;
        this.buttonToggle();
    //  页面复用时Key值
        this.change=!this.change;
        if(this.$route.path.indexOf("con")>0){
    
    //      获取list中第几个
            var typeData=this.$route.query.type;
            this.$nextTick(e=>{
                this.$http.get("static/data-"+listData+".json").then(rea=>{
                    this.loading=true;
                    setTimeout(e=>{
                                        
                        //vue-resource加载数据存在于data.body中
                        var listNum=rea.body.allData.slice(numData*6,numData*6+6);
                                    
                        //详细显示页面数据来源
                        this.allData.detailedData=listNum.slice(typeData,typeData+1)[0];
                                    
                        this.loading=false;
                    },700);
                });                
            });
            this.allData.mainShow=false;
        }else{
            this.$nextTick(e=>{
                this.loading=true;
                    setTimeout(e=>{
                        this.$http.get("static/data-"+listData+".json").then(rea=>{
                            this.allData.showData=rea.body.allData.slice(numData*6,numData*6+6);
                            this.loading=false;
                        });
                    },700);
                });
            this.allData.mainShow=true;
        }
    }

    这个方法的作用是交互数据,demo 有俩个分类,首页,和第一页,所以对应的数据也是俩个JSON。定时器的作用是模拟数据请求延时。

    以上就是 methods 方法里全部函数,下面解释一下Vue实例里其他的方法。

    created(){
        this.routePath();
    }

    这是一个生命周期钩子代表Vue实例被创建好了,创建好之后进行url解析,这是初始化的步骤,第一次打开这个demo执行的函数。官方文档:Vue生命周期

    watch:{
        "$route"(to){
            this.routePath();
        }
    }

    说到 watch 了这是监控url变化时触发的函数,说白了就是执行 router.path("/user/x/x") 
    之后Vue会检测到变化,从而进行回调函数,这里执行 routerPath() 分析 url 是属于哪个页面从而进行数据更新。

    好了,javascript的编写到此结束,主要部分还是在 routerPath() 这个函数,再通过 url 重新获取数据。


    style 部分就不说了,简单的css3动画

     利用vue-cli配合vue-router搭建一个完整的spa流程(二)

    在(一)中写到了主要页面的编写,现在开始三个路由页面的编写。

    第一步: 路由实例index.js

    先贴出代码。

    import Vue from 'vue'
    import Router from 'vue-router'
    import VueResource from 'vue-resource'
    import tem from '@/components/showone'
    import tem_cont from '@/components/showtwo'
    import tem_error from '@/components/error'
    
    //安装插件
    Vue.use(Router)
    Vue.use(VueResource)
    
    export default new Router({
        routes:[
            {
                path:"/user/:list/:listNum",
                component:tem,
                children:[
                    {
                        path:"con",
                        component:tem_cont
                    }
                ]
            },
            {
                path:"/user/error",
                component:tem_error
            }
        ]
    })

    代码很短,一一解释下。

    import Vue from 'vue'
    import Router from 'vue-router'
    import VueResource from 'vue-resource'
    import tem from '@/components/showone'
    import tem_cont from '@/components/showtwo'
    import tem_error from '@/components/error'

    ↑ 这里是引入所有使用的数据,参数。

    //安装插件
    Vue.use(Router)
    Vue.use(VueResource)

    ↑ 这里说下 vue-resource 这个一开始没有安装,打开项目右键打开Git 键入 npm install vue-resource --save
    这是一个ajax插件,使用起来比较方便而且很简单。

    routes:[
        {
            path:"/user/:list/:listNum",
            component:tem,
            children:[
                {
                    path:"con",
                    component:tem_cont
                }
            ]
        },
        {
            path:"/user/error",
            component:tem_error
        }
    ]

    ↑ 路由配置,详情页面是主页面的子路由。

    第二部: 三个路由xxxx.vue文件编写

    Ⅰ: showone.vue

    先贴出代码,有些复杂,慢慢解释。

    <template>
        <div style='cursor:pointer;height: 867px;' >
            <div v-show='routerData.mainShow'>
                <nav class='navbar navbar-default'>
                    <div class='container' style="padding: 0;">
                        <div class='collapse navbar-collapse' style="padding: 0;">
                            <ul class='nav navbar-nav userNav'>
                                <li><a v-bind:class="{activeBg:isActive==0}" v-on:click='link(0)'>首页</a></li>
                                <li><a v-bind:class="{activeBg:isActive==1}" v-on:click='link(1)'>页面一</a></li>
                            </ul>
                        </div>
                    </div>
                </nav>
                <div class='media' v-for='(item,index) in routerData.showData' v-on:click='go(item,index)'>
                    <div>
                        <div class='media-left'>
                            <img class='media-object' v-bind:src='item.thumbnail'>
                        </div>
                        <div class='media-body'>
                            <h2 class='media-heading' v-text='item.title'></h2>
                            <span style='font-size:25px;color:#ccc'>{{item.content | more}}</span>
                        </div>
                    </div>
                </div>
            </div>
            <router-view v-bind:router-nesting='routerData'></router-view>
        </div>
    </template>
    <script>
        import router from '.././router'
        export default{
            props:["routerData"],
            data(){
                return {
                    isActive:this.$route.path.slice(6,7)
                }
            },
            methods:{
                go(obj,index){
                    router.push({path:this.$route.path+"/con",query:{type:index}});
                },
                link(num){
                    var listNum=this.$route.path.slice(6,7);
                    if(listNum!=num){
                        router.push("/user/"+num+"/0");
                        this.isActive=this.$route.path.slice(6,7);
                    }
                }
            },
            filters:{
                more(value){
                    var newMessage=value.slice(0,40)+"........点击查看更多";
                    return newMessage;
                }
            }
        }
    </script>
    <style>
        .activeBg{
            background-color: teal;
            color: white !important;
            transition: 0.5s all ease-in;
        }
        .userNav :hover{
            background-color: cornsilk;
            color: black !important;
        }
    </style>

    template有俩部分组成:

    <div v-show='routerData.mainShow'>
        <nav class='navbar navbar-default'>
            <div class='container' style="padding: 0;">
                <div class='collapse navbar-collapse' style="padding: 0;">
                    <ul class='nav navbar-nav userNav'>
                        <li><a v-bind:class="{activeBg:isActive==0}" v-on:click='link(0)'>首页</a></li>
                        <li><a v-bind:class="{activeBg:isActive==1}" v-on:click='link(1)'>页面一</a></li>
                    </ul>
                </div>
            </div>
        </nav>
        <div class='media' v-for='(item,index) in routerData.showData' v-on:click='go(item,index)'>
            <div>
                <div class='media-left'>
                    <img class='media-object' v-bind:src='item.thumbnail'>
                </div>
                <div class='media-body'>
                    <h2 class='media-heading' v-text='item.title'></h2>
                    <span style='font-size:25px;color:#ccc'>{{item.content | more}}</span>
                </div>
            </div>
        </div>
    </div>
    

    ↑ 这是第一部分,包含导航与当前分类中全部内容的一个列表。

    ①: v-show='routerData.mainShow' 这个控制整体部分显示隐藏,与 上一页,下一页按钮为相同的控制数据,因为二者显示,隐藏逻辑是一样的。都是在详情页隐藏,主页显示。

    ②: v-on:click='link(0)' 导航按钮跳转绑定的函数。

    ③: v-for='(item,index) in routerData.showData' 循环数据,渲染列表。

    ④: v-on:click='go(item,index)' 每个列表绑定跳转到详情页的函数。

    ⑤: {{item.content | more}} 渲染简介,并且通过一个过滤器使内容中数字过多时,进行剪切


    <router-view v-bind:router-nesting='routerData'></router-view>

    ↑ 这是第二部分,子路由入口。v-bind:router-nesting='routerData' 给子路有中渲染页面的数据。

    接下来是script部分

    首先引入router实例 import router from '.././router'
    再接收 zjapp.vue 传输过来的数据 props:["routerData"]


    methods方法里函数解释:

    go(obj,index){
        router.push({path:this.$route.path+"/con",query:{type:index}});
    }

    ↑ 这是列表中内容点击时执行的函数,从 template 中传过来 index 值,添加到 url 中,从而获取这是列表中第几个。

    link(num){
        var listNum=this.$route.path.slice(6,7);
        if(listNum!=num){
            router.push("/user/"+num+"/0");
            this.isActive=this.$route.path.slice(6,7);
        }
    }

    ↑ link(num)函数是导航点击绑定的函数,通过传志 num 将 url 转换为对应的分类,从而触发 watch 重新获取数据。这里加了一个判断,重复点击,无效。

    filters:{
        more(value){
            var newMessage=value.slice(0,40)+"........点击查看更多";
            return newMessage;
        }
    }

    ↑ 过滤器,剪切字数。


    style就不解释了

    Ⅱ: showtwo.vue

    这个是文件是详情页面,即主页面中的列表内容点击后,跳转的页面。

    <template>
        <div style='height:700px;padding-top:100px'>
            <div>
                <img v-bind:src='routerNesting.detailedData.src' style='display:block;margin:0 auto;500px'>
                <br>
                <h2 v-text='routerNesting.detailedData.title'></h2>
                <p>{{routerNesting.detailedData.content}}</p>
                <button v-on:click='back' class='btn btn-success'>返回</button>
            </div>
        </div>
    </template>
    <script>
        import router from '.././router'
        export default{
            props:["routerNesting"],
            methods:{
                back(){
                    router.push(this.$route.path.slice(0,9));
                }
            }
        }
    </script>
    <style></style>

    ↑ 数据与showone.vue相似,routerNesting数据是通过ziapp.vue->showone.vue->showtwo.vue传递过来的。
    back() 函数将url从/user/0/0/con?type=2跳转到/user/0/0 触发watch更新数据。

    Ⅲ: error.vue

    <template>
        <div style="margin-top: 150px;">
            <h1 style="text-align: center;">遥远的404!</h1>
            <div style=" 150px;margin: 50px auto;">
                <button v-on:click="jump(0)" class="btn btn-primary">首页</button>
                <button v-on:click="jump(1)" class="btn btn-primary">第一页</button>
            </div>
        </div>
    </template>
    <script>
        import router from '.././router'
        export default{
            methods:{
                jump(num){
                    num==0?router.push("/user/0/0"):router.push("/user/1/0");
                }
            }
        }
    </script>
    <style></style>

    ↑ 嗯~ o( ̄▽ ̄)o,这个比较简单,不做解释了。


    结语

    至此,全部都解清楚了,按照步骤来的话一个简单的spa也初见其形。

    整体思想:通过watch监控url的变化,变化后执行routerPath()函数,随后重新获取数据。

  • 相关阅读:
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 454 四数相加 II
    Java实现 LeetCode 454 四数相加 II
    Java实现 LeetCode 454 四数相加 II
    FFmpeg解码H264及swscale缩放详解
    linux中cat more less head tail 命令区别
    C语言字符串操作总结大全(超详细)
    如何使用eclipse进行嵌入式Linux的开发
  • 原文地址:https://www.cnblogs.com/sweeeper/p/8652624.html
Copyright © 2011-2022 走看看