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()函数,随后重新获取数据。

  • 相关阅读:
    ISAPI_Rewrite规则写法
    用Windows 2003配置邮件服务器
    如何保护.net中的dll文件(防破解、反编译)
    The Tao Of Programming 编程之道
    Request.ServerVariables (server environment variable)
    Windows下搭建Subversion 服务器
    邮件服务器反解析退信问题分析
    关于公网和内网之间Socket的通信
    .net安装部署中添加卸载程序简单方法
    构建高性能ASP.NET站点 剖析页面处理过程
  • 原文地址:https://www.cnblogs.com/sweeeper/p/8652624.html
Copyright © 2011-2022 走看看