欲学乾坤大挪移,必先搞定九阳神功!

1. 大学为什么先学C语言?

大学计算机系大一课程中,C语言几乎是标配语言。一个学校是否以C语言作为启蒙计算机语言,很大程度上决定了这个学校的教学品质。
为什么这么说呢?因为C语言是最基础(从当代学习计算机编程角度)的计算机语言,把C语言学透,学扎实,后面学任何新语言都会很快。

这点上看是不是很像《倚天屠龙记》里面张无忌学习“九阳神功”和“乾坤大挪移”的桥段?

《倚天屠龙记》里面张无忌被困明教密道的时候,在小昭的帮助下偶得“乾坤大挪移”心法。张无忌在几个时辰里面学会了别人一生都不一定能学会的“乾坤大挪移”。

书中说的很明白,因为张无忌学习了几年的“九阳神功”,明白了很多学习高等武功的基础知识,再学习高等武功的时候便能融会贯通。

那些没有“九阳神功”基础的人,拿到“乾坤大挪移”后只能看的云里雾里,里面的武功招式看似相识,却又非常陌生,无法实际操练。

2.前端程序员先搞定ES6!

之所以提出这个话题,是因为最近学习前端技术的时候感触颇深。

这些年前端火的不得了,所以就决定自学Node.js,Vue.js,Koa2,Egg.js等等技术。由于自己有着很多年Javascript的基础,觉得直接上手这些技术应该不是难事儿。

最开始学了几天Node.js,觉得一知半解就开始学Vue.js,更加一知半解,然后进入Egg.js,基本上就蒙了。

学习的整体感觉就是,觉得里面的东西都是Javascript,觉得都似曾相识,但是就是觉得别扭,就是记不住。

然后重新梳理一遍,Egg.js基于Koa2,Koa2又基于Node.js,而这一切的一切,都是基于ES6。

于是花了点时间学习ES6,再学习Node.js等其它技术的时候,之前看的云里雾里的一下子豁然开朗。

3. 欲学乾坤大挪移,必先搞定九阳神功!

所以同学们,面对新技术,新知识,千万不要急。

在学习前,一定要先把学习顺序和学习方法搞清楚。

欲成就“乾坤大挪移”大业,必先搞定“九阳神功”!

Koa,Koa2入坑

1. 什么是Koa,Koa2

  • Koa是Express的竞争品。Koa2是Koa的升级版本。
  • Koa和Koa2与Express一样,都是基于Node.js开发的Web框架。
  • 在koa中,一切的流程都是中间件,数据流向遵循洋葱模型,先入后出,是按照类似堆栈的方式组织和执行的,koa-compose是理解koa中间件的关键,在koa中间件中会深入分析。
  • koa2与koa1的最大区别是koa2实现异步是通过async/awaite,koa1实现异步是通过generator/yield,而express实现异步是通过回调函数的方式。
  • koa2与express 提供的API大致相同,express是大而全,内置了大多数的中间件,更让人省心,koa2不绑定任何的框架,干净简洁,小而精,更容易实现定制化,扩展性好。
  • express是没有提供ctx来提供上下流服务,需要更多的手动处理,express本身是不支持洋葱模型的数据流入流出能力的,需要引入其他的插件。
    以上内容参考:https://www.jianshu.com/p/a518c3d9c56d

2. Koa的安装和第一个Koa程序。

2.1 Koa安装

cmd进入工作目录,运行如下命令

cpmn install koa--save

2.2 第一个Koa程序

在工作目录新建app.js,并输入以下代码:

const koa = require("koa");
const app = new koa();

app.use(
    async (ctx) => {
        ctx.body="Hello Koa!"
    }
);

app.listen(3000);

在cmd中运行:

node app.js

浏览器中输入 localhost:3000 便可以访问使用koa建立的web服务器了。

3. Koa路由

3.1 Koa路由功能安装。

Koa的路由功能不是标配(这点和Express不同),需要在安装Koa的基础上安装路由模块Koa-router。
cmd切换到工作目录后使用以下命令安装:

cnpm install koa-router --save

3.2 第一个路由示例

const Koa = require("koa");
const Router = require("koa-router");

var app = new Koa();
var router = new Router();

//逐条指定方式
router.get("/",async(ctx)=>{ //ctx包含request和require等信息.
    ctx.body = "这里是首页";
});
router.get("/news",async(ctx)=>{
    ctx.body = "这里是新闻页面";
});

//连续简写方式
router.get("/list",async(ctx)=>{
    ctx.body = "这里是列表页面";
}).get("/details",async(ctx)=>{
    ctx.body = "这里是细节页面";
});

app.use(router.routes());//使用中间件:启动路由
app.use(router.allowedMethods());//使用中间件:设置响应头,比如页面出错的时候返回404

app.listen(3000,()=>{
    console.log("http://localhost:3000 is ready!");
});

3.3 动态路由

其实更好理解的方式就是通过路由(URL)传参。

const Koa = require("koa");
const Router = require("koa-router");

var app = new Koa();
var router = new Router();

router.get("/product/:name/:brand/:price",(ctx)=>{
    console.log(ctx.params);
    ctx.body="产品页面!";
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000,()=>{
    console.log("http://localhost:3000 is ready!");
});

访问 http://localhost:3000/page?name=Tom&gender=male ,cmd中结果如下:
file

4. Koa中间件

中间件的概念类似于其它语言中的过滤器。
Koa中间件的运行流程采用的洋葱模式。

const Koa = require("koa");
const Router = require("koa-router");

var app = new Koa();
var router = new Router();

//应用级中间件,app.use("可选路由",函数)
//如果指定路由,则只对目标路由有效,如果不指定,则对所有路由有效.
app.use(async (ctx,next)=>{
    ctx.body="This is the middleware";
    await next();//如果没有next()指令,则其它的代码不会再执行.
});
router.get("/",async(ctx)=>{
    ctx.body += "This is the index page!";//所以呢,为了后期使用中间件更方便,即便初次指定ctx.body,也建议使用+=这种赋值方法.
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000,()=>{
    console.log("http://localhost:3000 is ready!");
});

5. ejs模板引擎的使用

5.1 安装koa-views和ejs

cnpm install koa-views --save
cnpm install ejs --save

5.2 第一个ejs示例

本示例共有三个文件。
第1个文件:/根目录/koa-views.js,代码如下:

const Koa = require("koa");
const Router = require("koa-router");
const Views = require("koa-views");

var app = new Koa();
var router = new Router();

app.use(Views("./views",{ //   ./views 指明模板文件夹位置
    //extension:"ejs"    //方案一:识别.ejs文件.
    map:{html:'ejs'}   //方案二:将.html视为.ejs文件,可以在html文件中撰写ejs代码.
}));

app.use(async(ctx,next)=>{   //配置公共变量信息
    ctx.state.userinfo = "Tom";
    await next();
});

router.get("/fejs",async(ctx)=>{
    let title = "hello EJS";
    let arr = ["111","222","333"];
    await ctx.render("firstEJS",{ //await必须加
        title:title,
        list:arr
    });
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000,()=>{
    console.log("http://localhost:3000 is ready!");
});

第2个文件:/根目录/views/firstEJS.html,代码如下:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <%- include('header.html') -%> <!--注意:最新版本的EJS需要用这种语法引入文件-->
        <h2><%=title%></h2>
        这里是EJS文件
        <ul>
            <%for(var i=0;i<list.length;i++){%>
                <li><%=list[i]%></li>
            <%}%>
        </ul>
        <%=userinfo%>
    </body>
</html>

第3个文件:/根目录/views/header.html,代码如下:

<h1>这里是头部内容!</h1>

命令行运行:

node koa-view.js

浏览器中打开 http://localhost:3000 显示结果如下:
file

6. 获取get和post数据。

6.1 获取get数据

通过ctx.query

const Koa = require("koa");
const Router = require("koa-router");

var app = new Koa();
var router = new Router();

router.get("/page",async(ctx)=>{
    console.log(ctx.query);
    console.log(ctx.querystring);
    ctx.body = "hello page!";
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000,()=>{
    console.log("http://localhost:3000 is ready!");
});

访问 http://localhost:3000/page?name=Tom&gender=male ,cmd中结果如下:
file

6.2 获取post数据。

6.2.1 安装koa-bodyparser

cnpm install koa-bodyparser --save

6.2.2 获取post数据示例

本示例有两个文件:
第1个文件:/根目录/postdata.js

const Koa = require("koa");
const Router = require("koa-router");
const Views = require("koa-views");
var bodyParser = require("koa-bodyparser");

var app = new Koa();
var router = new Router();

app.use(Views("./views",{ //   ./views 指明模板文件夹位置
    //extension:"ejs"    //方案一:识别.ejs文件.
    map:{html:'ejs'}   //方案二:将.html视为.ejs文件,可以在html文件中撰写ejs代码.
}));

router.get("/",async(ctx,next)=>{
    await ctx.render("index");
});

app.use(bodyParser());
//获取post数据
router.post("/doAdd",async(ctx)=>{
    ctx.body = ctx.request.body;
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000,()=>{
    console.log("http://localhost:3000 is ready!");
});

第2个文件:/根目录/views/index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <form action="/doAdd" method="post">
            <input type="text" name="username" />
            <input type="password" name="password"/>
            <input type="submit" value="提交"/>
        </form>
    </body>
</html>

在浏览器中访问 http://localhost:3000 随意填写数据后提交,结果如下:
file

7. koa-static静态资源中间件

7.1 安装koa-static

cnpm install koa-static --save

7.2 第一个koa-static示例

与express的static非常相似。
本示例有三个示例文件。
文件1:/根目录/view/index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <link rel="stylesheet" type="text/css" href="./css/style.css"/>
    </head>
    <body>
        你好!
    </body>
</html>

文件2:/根目录/static/css/style.css

body{
    font-size:50px;
}

文件3:/根目录/index.js

const Koa = require("koa");
const Router = require("koa-router");
const Views = require("koa-views");
const Static = require("koa-static");

var app = new Koa();
var router = new Router();

app.use(Views("./views",{ //   ./views 指明模板文件夹位置
    //extension:"ejs"    //方案一:识别.ejs文件.
    map:{html:'ejs'}   //方案二:将.html视为.ejs文件,可以在html文件中撰写ejs代码.
}));

router.get("/",async(ctx,next)=>{
    await ctx.render("index");
});

app.use(Static("static"));

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000,()=>{
    console.log("http://localhost:3000 is ready!");
});

8. art-template模板引擎的使用

art-template是ejs的竞争品,用法和ejs非常类似。只不过速度比ejs要快。

8.1 art-template安装

cnpm install art-template --save
cnpm install koa-art-template --save

8.2 art-tempalte示例

本示例有三个文件:
文件1:/根目录/art_template.js

const Koa = require("koa");
const Router = require("koa-router");
const render = require("koa-art-template");

var app = new Koa();
var router = new Router();

render(app,{
    root:"views",//目录
    extname:".html",//文件名后缀,可以任意指定
    debug:process.env.NODE_ENV !== "production"//是否开启调试模式
});

router.get("/",async(ctx)=>{
    let list = {
        name:"Tom"
    };
    await ctx.render("index",{
        list:list
    });
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000,()=>{
    console.log("http://localhost:3000 is ready!");
});

文件2:/根目录/views/index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        {{include "./header.html"}}
        <br/> 
        <% include("./header.html") %>
        <br/>
        你好!
        <br/>
        {{list.name}}           <!--两种模板语法都可以-->
        <br/>
        <%=list.name%>            <!--两种模板语法都可以-->
    </body>
</html>

文件2:/根目录/views/header.html

<h1>这里是头部内容!</h1>

9. cookie和session

Javascript的异步机制

0. 异步入坑。

讨论这个问题一定是在入坑Node.js后,而且很有可能是在入坑Express和Koa后。在ES5和ES6中,与异步相关的机制主要涉及以下几个概念:

  • 回调函数(ES5)
  • Promise + then(ES6)
  • Generator + next(ES6)
  • async + await(ES6)

1. 回调函数

Javascript异步操作最基本的解决方案。
由于name的定义是异步的,存在1秒的时延,所以name变量只有通过回调函数才能获取到。

function getData(callback){
    setTimeout(()=>{
        var name = "Tom";
        callback(name);
    },1000);
}
getData((data)=>{
    console.log(`Name is ${data}`);
});

2. Promise + then

ES6异步的另一种解决方案。和回调函数的语法和逻辑最为相像。

function getData(resolve,reject){
    setTimeout(()=>{
        try{
            var name = "Tom";
            resolve(name);
        }catch(e){
            reject(e);
        }
    },1000);
}
var p = new Promise(getData);
p.then((data)=>{
    console.log(`My name is ${data}!`);
});

上面的代码也可以合并简化一下:

var p = new Promise(function(resolve,reject){
    setTimeout(function(){
        try{
            var name = "Tom";
            resolve(name);
        }catch(e){
            reject(e);
        }
    },1000);
});
p.then((data)=>{
    console.log(`My name is ${data}!`);
});

3. Generator + next

3.1 基本概念。

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();

上面代码定义了一个 Generator 函数helloWorldGenerator,它内部有两个yield表达式(hello和world),即该函数有三个状态:hello,world 和 return 语句(结束执行)。

然后,Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。

下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。

hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }

上面代码一共调用了四次next方法。

第一次调用,Generator 函数开始执行,直到遇到第一个yield表达式为止。next方法返回一个对象,它的value属性就是当前yield表达式的值hello,done属性的值false,表示遍历还没有结束。

第二次调用,Generator 函数从上次yield表达式停下的地方,一直执行到下一个yield表达式。next方法返回的对象的value属性就是当前yield表达式的值world,done属性的值false,表示遍历还没有结束。

第三次调用,Generator 函数从上次yield表达式停下的地方,一直执行到return语句(如果没有return语句,就执行到函数结束)。next方法返回的对象的value属性,就是紧跟在return语句后面的表达式的值(如果没有return语句,则value属性的值为undefined),done属性的值true,表示遍历已经结束。

第四次调用,此时 Generator 函数已经运行完毕,next方法返回对象的value属性为undefined,done属性为true。以后再调用next方法,返回的都是这个值。

3.2 异步

function* gen(x) {
  var y = yield x + 2;
  return y;
}

var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }

上面代码中,调用 Generator 函数,会返回一个内部指针(即遍历器)g。这是 Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针g的next方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的yield语句,上例是执行到x + 2为止。

换言之,next方法的作用是分阶段执行Generator函数。每次调用next方法,会返回一个对象,表示当前阶段的信息(value属性和done属性)。value属性是yield语句后面表达式的值,表示当前阶段的值;done属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。

4. async + await

async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

async function getStockPriceByName(name) {
  const symbol = await getStockSymbol(name);
  const stockPrice = await getStockPrice(symbol);
  return stockPrice;
}

getStockPriceByName('goog').then(function (result) {
  console.log(result);
});

上面代码是一个获取股票报价的函数,函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。(此部分参考:https://es6.ruanyifeng.com)

移动端开发的一些常用知识

1. 字体的px,em和rem

1.1 px

px是固定的像素,一旦设置了就无法因为适应页面大小而改变。

1.2 em

  • 子元素字体大小的em是相对于父元素字体大小
  • 元素的width/height/padding/margin用em的话是相对于该元素的font-size

1.3 rem

rem中的r代表的是是root。
rem是全部的长度都相对于根元素,根元素是谁?<html>元素。
通常做法是给html元素设置一个字体大小,然后其他元素的长度单位就为rem。

1.4 总结

在做项目的时候用什么单位长度取决于具体的需求,但一般是这样的:

  • 像素(px):用于元素的边框或定位。
  • em/rem:用于做响应式页面,不过我更倾向于rem,因为em不同元素的参照物不一样(都是该元素父元素),所以在计算的时候不方便,相比之下rem就只有一个参照物(html元素),这样计算起来更清晰。

2. 分辨率,PPI,DPI,DPR的概念解释

2.1 分辨率

是指设备屏幕宽度上和高度上最多能显示的物理像素点个数。

2.2 PPI

PPI:Pixel Per Inch。屏幕像素密度,即每英寸(1英寸=2.54厘米)聚集的像素点个数,这里的一英寸是对角线长度。

2.3 DPI

DPI:Dots Per Inch。每英寸像素点,印刷行业标准。衡量打印机精度以及图片精度的标准。

2.4 DPR

DPR:Device Pixel Ratio。是默认缩放为100%的情况下,设备物理像素和CSS像素的比值。
举个例子:
这里先无限diss B站中的某个傻X老师,乱讲一通,害得我思考了好久。
iPhone6的CSS的物理像素是:750x1334。
iPhone6默认的DPR=2,那么其默认的css逻辑像素就375x667。
这样的结果是什么呢?结果就是,如果你在代码中写了

<div style="width=375px;height:667px"></div>

这个div就会充满iPhone的屏幕。相当于css中的1px占用了手机的2个像素。

2.5 应用

2.5.1 有了以上的概念,我们便可以通过动态viewport设置页面,使css像素对应设备屏幕物理像素。
<script type="text/javascript">
        var scale = 1/window.devicePixelRatio;
        document.write(`
            <meta name="viewport" content="initial-scale=${scale},minimum-scale=${scale},maximum-scale=${scale},user-scalable=no" />
        `);
</script>
2.5.2 还可以模仿微信对屏幕的丈量标准,将屏幕宽度分为750份。
<script type="text/javascript">
    var unit = window.innerWidth / 750; //将屏幕分为750个单位
    document.write(`
        <style>
            html{
                font-size:$(100*unit)px; /*根元素字体大小是100个单位*/
            }
            .zhengWen{
                font-size:0.05rem; /*正文字体大小是5个单位*/
            }
        </style>
    `);
</script>

Vue.js随学随记

1. v-bind和v-model理解

1.1 v-bind

v-bind代表元素的某个属性交给vue处理,实现元素属性和vue的绑定,v-bind="表达式",引号里面的本质是表达式。但是这种绑定是单向的。比如下面的代码,当你在网页中修改input中值的时候,上面的message内容不会随着修改。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script type="text/javascript" src="vue.js"></script>
    </head>
    <body>
        <div id="app">
            <div>{{message}}</div>
            <div>
                <input type="text" v-bind:value="message"/>
            </div>
        </div>

        <script type="text/javascript">
            var vm = new Vue({
                el:"#app",
                data:{
                    message:"Welcome to Vue"
                }
            });
        </script>
    </body>
</html>

如果想实现双向绑定,还需要向下方一样为标签添加v-on指令,这样在修改input里面内容的时候,上面message的内容也会相应的修改。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script type="text/javascript" src="vue.js"></script>
    </head>
    <body>
        <div id="app">
            <div>{{message}}</div>
            <div>
                <input type="text" v-on:input="message = $event.target.value" v-bind:value="message"/>
            </div>
        </div>
        <script type="text/javascript">
            var vm = new Vue({
                el:"#app",
                data:{
                    message:"Welcome to Vue"
                }
            });
        </script>
    </body>
</html>

1.2 v-model

v-model实际上是v-bind:和v-on:的语法糖,实现了简易的元素属性双向绑定。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script type="text/javascript" src="vue.js"></script>
    </head>
    <body>
        <div id="app">
            <div>{{message}}</div>
            <div>
                <input type="text" v-model="message"/>
                <!--上方代码等同于下方代码
                <input type="text" v-on:input="message = $event.target.value" v-bind:value="message"/>
                -->
            </div>
        </div>
        <script type="text/javascript">
            var vm = new Vue({
                el:"#app",
                data:{
                    message:"Welcome to Vue"
                }
            });
        </script>
    </body>
</html>

2. v-on:click 事件调用函数的问题。

2.1 方法一:直接调用函数名

<button v-on:click="change">改变一下</button>

这种情况下,函数被调用时,自动传递$event参数。

2.2 方法二:调用函数名()

这种情况下,函数被调用时,不会自动传递$event参数,如果需要,则必须人工指定为函数名($event)。也就是说下面的代码等同于上面的代码:

<button v-on:click="change($event)">改变一下</button>

3. v-if和v-show的区别。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script type="text/javascript" src="vue.js"></script>
    </head>
    <body>
        <div id="app">
            <button type="button" v-if="isShowButton">使用v-if判断</button>
            <button type="button" v-show="isShowButton">使用v-show判断</button>
        </div>
        <script type="text/javascript">
            var vm = new Vue({
                el:"#app",
                data:{
                    isShowButton:false
                }
            });
        </script>
    </body>
</html>

运行结果如下:
file

  • 对于v-if,如果=后面的表达式是false,则整个标签不会显示。
  • 对于v-show,如果=后面的表达式是false,则会以display:none的形式隐藏标签。

4. v-for对数组和对象的使用方法。

4.1 示例代码

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script type="text/javascript" src="vue.js"></script>
    </head>
    <body>
        <div id="app">
            <table border="1" cellspacing="0" cellpadding="0">
                <tr><th>姓名</th><th>性别</th><th>分数</th></tr>
                <tr v-for="student,arrayIndex in students">
                    <td v-for="value,key,objectIndex in student">{{arrayIndex}} - {{objectIndex}} - {{key}} - {{value}}</td>
                </tr>
            </table>
        </div>
        <script type="text/javascript">
            var vm = new Vue({
                el:"#app",
                data:{
                    students:[
                        {name:"张三",gender:"男",score:"100"},
                        {name:"李四",gender:"女",score:"88"},
                        {name:"王五",gender:"男",score:"99"}
                    ]
                }
            });
        </script>
    </body>
</html>

运行结果如下:
file

4.2 对于数组

<tr v-for="student,arrayIndex in students">
</tr>

遍历students数组的每一个元素。

4.3 对于对象

<td v-for="value,key,objectIndex in student">{{arrayIndex}} - {{objectIndex}} - {{key}} - {{value}}</td>

遍历student对象的每一个属性。

5. Vue-cli 3.0 的使用

5.0 理解Vue-cli的关键点

vue-cli是将Node.js和webpack封装进了vue-cli内部。

5.1 安装Vue-cli 3.0

cnpm install -g @vue/cli

需要注意的是,如果之前安装过Vue-cli的2.X版本,需要先卸载之前的版本再安装。卸载方法在安装Vue-cli 3.0的时候有提示,这里不赘述。

5.2 创建项目

安装完Vue-cli 3.0工具后,我们在命令行里面就可以直接使用vue命令了,下面的命令用于创建一个Vue.js的项目框架:

vue create 项目名

这里面有几个注意点:

  1. vue create 项目名命令只对Vue-cli 3.0及以后版本有效,2.X版本无法使用vue create命令
  2. 运行这个命令后,会自动在cmd当前目录下创建【项目名】文件夹,然后在里面生成Vue.js的项目框架。
  3. 这里要特别解释下“脚手架”这个词。初次学Vue.js的时候,对“脚手架”工具特别不理解,不明白啥叫脚手架。说白了,“脚手架”就是辅助工具。Vue-cli的作用就是辅助进行大型的Vue.js项目开发。如果只是在项目中一般性的使用Vue.js,在网页中使用script引入Vue.js文件就够了,不是必须使用Vue-cli脚手架工具。
  4. 项目创建好后,项目的目录结构如下:
    file
    从上面的截图能看出来,其实vue create命令创建的是个node.js项目。其目的和原因也很简单:通过Node.js模拟服务器运行及热更新环境,这样可以让开发过程更加简单、高效。所以,如果要使用Vue-cli工具,前提是机器中必须提前安装了Node.js(如果不使用Vue-cli工具而是只用Vue.js,可以没有Node.js环境)。

5.3 构建项目

既然vue create项目创建的是Node.js项目,那么当时用npm run命令的时候,就可以直接调用package.json里面<script>标签下的三个命令。

5.1.1 serve命令:开发环境构建
npm run serve

这一步最主要的操作是通过Node.js启动一个Web服务器,这样可以直接在本地模拟服务器环境。

5.1.2 build命令:生产环境构建
npm run build

运行这个命令后,会在项目根目录生成一个【dist】文件夹,这里面的文件是经过编译、打包、压缩后,最终用于部署到服务器上的代码。
实际上,这个操作是webpack做的,vue-cli只是把webpack封装进去了。。

5.1.3 lint命令:代码检查工具
npm run lint

6 vue-router路由库使用

6.1 关于SPA

vue-router路由一般使用在SPA(Single Page Application)单页面程序上。SPA的简单的来说就是无论程序有多复杂,所有程序代码都写在一个页面上。
当然,在开发阶段,SPA项目开始分模块开发的,开发完毕后会通过构建工具将各类模块组成一个单页面程序。
SPA的有点有很多,比如:一次加载后,页面无需再跳转和下载新内容,页面速度快;再比如:有效降低网页被复制和爬取的风险。缺点也很明显,搜索引擎索引起来不是很好。不过SPA程序一开始就不是为搜索引擎而设计的。

6.2 vue-router

vue-router是vue.js的官方路由工具。第三方路由如Page.js、Director也很好用,与vue.js的集成也比较容易。

6.3 vue-router的安装

cnpm install vue-router --save

或者直接下载vue-router.js导入到项目中。

6.4 vue-router的使用

6.4.1 HTML代码
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!-- 使用 router-link 组件来导航. -->
    <!-- 通过传入 `to` 属性指定链接. -->
    <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>
6.4.2 Javascript代码
// 0. 如果使用模块化机制编程,导入 Vue 和 VueRouter,要调用 Vue.use(VueRouter)

// 1. 定义(路由)组件。
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }

// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar }
]

// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
  routes // (缩写)相当于 routes: routes
})

// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
  router
}).$mount('#app')

// 现在,应用已经启动了!

当然,上面代码也可以稍微简化一下:

const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }

const router = new VueRouter({
  routes = [
    { path: '/foo', component: Foo },
    { path: '/bar', component: Bar }
  ]
})

const app = new Vue({
  router
}).$mount('#app')

Object.defineProperty的应用

1. 解释

使用Object.defineProperty(对象,属性名,GetSet方法对象),可以为某个对象设置属性和属性值。
在为对象设置属性值和提取属性值的时候,都可以添加额外功能。

2. 代码示例

var obj = {
    name:"Zhang"
};

var ageValue;

Object.defineProperty(obj,"age",{
    get(){
        console.log("正在获取age属性");
        return ageValue;
    },
    set(value){
        console.log("正在设置age属性");
        ageValue = value;
    }
});

console.log(obj.age);
obj.age = 100;
console.log(obj.age);

运行以上代码输出结果如下:
file

Windows下安装和配置MongoDB4.2.3及MongoDB基本命令

1. 下载安装文件。

MongoDB分为收费版和免费社区版,我们学习自然用免费就可以了,官网下载地址如下:
https://www.mongodb.com/download-center/community

2. 安装MongoDB

2.1 选择自定义安装

file

2.2 选择下安装目录

这里有个比较重要的地方,强烈建议安装目录中间不要有空格等非英文字符。因为在使用cmd的时候,带空格会导致不便。
file

2.3 这步默认即可

file

2.4 不要安装MongoDB Compass

这是个MongoDB的可视化管理软件,如果一起安装,安装过程奇慢。需要的可以后面自行安装。
file

2.5 安装完成后需要重启。

file

3. 连接MongoDB数据库

3.1 新建db目录

在安装目录以下位置新建空的db目录:MongoDB\data\db

3.2 开启MongoDB服务器

  1. cmd进入MongoDB目录的bin目录
  2. 运行命令:mongod --dbpath D:\ProgramData\MongoDB\data\db

如果出现以下内容,证明MongoDB服务已经开启:

file

3.3 连接MongoDB服务器

  1. cmd进入MongoDB目录的bin目录
  2. 运行命令:mongo.exe
  3. 使用以下任一命令测试是否连接成功:
db.stats()
db.help()
show dbs

4. MongoDB基本命令

4.0 名词“集合”和“文档”

集合:相当于SQL里面的表;
文档:相当于SQL里面表里面的一条数据;

4.1 db

显示当前所在的数据库。

4.2 db.stats()

显示当前数据库统计信息。

4.3 db.help()

帮助信息。

4.4 show dbs

显示MongoDB下的所有数据库名称(空数据库不显示,只显示有数据的数据库)。

4.5 use 数据库名

切换到相应的数据库。
如果数据库不存在,则新建这个数据库。

4.6 show collections

显示当前数据库里面的collection(类似MySQL的表)。空集合不显示,只显示有数据的集合。

4.7 db.集合名.find(条件)

db.student.find({})

上面代表:显示集合下的所有信息。

db.student.find({}).pretty()

上面代表:格式化显示集合下的所有信息。

db.student.find({price:120})

上面代表:条件查询,相当于SQL里面的where name = "张三"

db.student.find({price:{$gt:120}})

上面代表:条件查询,相当于SQL里面的where price > 123

db.student.find({price:{$gte:120}})

上面代表:条件查询,相当于SQL里面的where price >= 123

db.student.find({price:{$lt:120}})

上面代表:条件查询,相当于SQL里面的where price < 123

db.student.find({price:{$lte:120}})

上面代表:条件查询,相当于SQL里面的where price <= 123

db.student.find({price:{$ne:120}})

上面代表:条件查询,相当于SQL里面的where price != 123

db.student.find({
    $and:[
            {id:1},{name,"张三"}
    ]
})

上面代表:条件查询,相当于SQL里面的where price id=1 and name="张三"

4.8 db.dropDatabase()

删除当前数据库

4.9 skip和limit

db.student.find({}).skip(20).limit(10)

分页显示:跳过前面20条数据,显示10条数据。相当于分页显示第三页数据。

4.10 db.createCollection(集合名字,参数)

在当前数据库下创建一个集合

4.11 db.集合名.drop()

删除集合

4.12 db.集合名.insert({对象})

向集合中插入数据,比如:
db.product.insert({"productCategory":"电子产品","productName":"笔记本电脑"})

4.13 db.集合名.update(条件参数,新的数据)

db.age.update(
    {title:"张小三"},
    $set:{title:"张三",age:"18"}
}

更新集合中的数据,相当于SQL的:

update age set title="张三", age="18" where title="张小三"

4.14 db.集合名.remove(条件)

按条件删除集合中的某些数据

4.15 db.集合名.find().sort({属性:1或-1})

按照属性(列)进行排序,1为升序,-1位降序。

Node.js随学随记

0. Node.js动态文件编译的问题

在一切开始之前,先要明白一点:Node.js虽然使用Javascript语言编写,但是他的运行机制和Java、C++等编译型语言类似。也就是说,使用Node.js编写的代码如果要正常运行,是先要经历一个类似编译的过程的。
Node.js的这种运行机制所直接引发的结果就是:

  1. Node.js比普通Javascript运行速度要快。是否比Java和C++快我不清楚,但是至少应该比PHP快(因为PHP是解释性语言)。
  2. Node.js代码再修改后,必须重新运行node XXX.js命令后才能生效。

1. 利用NPM

npm有几个比较重要的命令:install,uninstall,init,run

1.1 install

带参数的install

当项目需要添加一个新的包的时候,使用npm install 包名称 --参数的形式,这时有四种安装方式可能:

npm install 包名称             //安装到当前cmd所在目录,不将包的依赖关系写入package.json文件
npm install 包名称 --save      //安装到当前cmd所在目录,将包的依赖关系写入package.json的dependencies节点
npm install 包名称 --save-dev  //安装到当前cmd所在目录,不将包的依赖关系写入package.json的devDependencies节点
npm install 包名称 -g          //安装到全局目录下,不将包的依赖关系写入package.json文件,且安装的这个包全局可用。

所以呢,对于Node.js使用的工具类包,比如:查询Node.js对ES6的支持度,使用--g是比较合适的,因为你肯定不希望每次使用这个工具的时候还要特意的将cmd目录切换到安装目录下。
而对于Node.js项目中使用的第三方类库比如express等,使用--save是比较合适的。因为这个类库只对当前项目有效。

不带参数的install

上面提到了,如果使用npm install 包名称 --save命令,会将安装的包的信息添加到package.json文件中。
package.json可以通过npm init命令创建,也可以在第一次使用npm install 包名称 --save命令的时候创建。
package.json文件有四个比较重要的节点信息:dependencies、devDependencies、script、main。

  • dependencies
    你的Node.js项目可能会通过npm install 包名称 --save命令安装很多包,当你把你的项目给别人的时候,你不需要把这些安装的包文件发给他们,因为你的这个包信息都写在了dependencies节点下面,拿到你项目文件的人只需要在项目目录中运行npm install,就可以安装所有package.json的dependencies节点下面的包了。这就极大的提升了项目和系统移植的便捷性。
  • devDependencies
    和dependencies节点的功能类似,只不过devDependencies节点下面的包只在你开发的时候有效,换句话说这些包是你在开发的时候临时用的。
  • script
    在项目目录中使用npm run 命令的时候,运行的实际上就是script节点下预先定义好的命令。
  • main
    指定整个Node.js项目的入口文件。

1.2 uninstall

卸载安装的包,如果这个包存在于package.json文件的dependencies节点中,相关信息将会被移除。

1.3 init

初始化一个新的Node.js项目,自动生成package.json文件架构。

1.4 run

npm run 命令
运行package.jsons文件cript节点下预先定义好的命令

2. events事件模块

events模块只提供了一个对象:events.EventEmitter。EventEmitter的核心就是事件触发事件监听

var events = require('events');   //引入events模块
var emitter = new events.EventEmitter();    //新建EventEmitter对象
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener1', arg1, arg2); 
}); //事件监听
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener2', arg1, arg2); 
}); //事件监听
emitter.emit('someEvent', 'byvoid', 1991); //事件触发。一个事件触发可以对应多个事件监听

3. fs文件系统模块

3.1 fs模块概述

Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()。
异步的方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。
建议大家使用异步方法,比起同步,异步方法性能更高,速度更快,而且没有阻塞。

3.2 readFile,writeFile

读取和写入文件,适用于读取和写入数据量不大的情况。如果读取写入数据量很大,建议使用后面的stream相关函数。

var fs = require("fs");
// 读取文件
fs.readFile('input.txt', function (err, data) {
   if (err) {
       return console.error(err);
   }
   console.log("读取: " + data.toString());
});
// 写入文件
fs.writeFile('input.txt', '我是通 过fs.writeFile写入文件的内容',  function(err) {
   if (err) {
       return console.error(err);
   }
   console.log("数据写入成功!");
});

3.3 createReadStream,createWriteStream,pipe

对于大型文件的输入输出,使用stream相关函数。这样可以将大文件切分为小的块通过stream进行传输。
先看数据读取:

var fs = require("fs");
var data = '';
// 创建可读流
var readerStream = fs.createReadStream('video.mp4');
// 数据读取进行时
readerStream.on('data', function(chunk) {
   console.log(chunk);//这里面打印的就是buffer信息。
});
// 数据读取结束时
readerStream.on('end',function(){
   console.log("程序执行完毕");
});
// 数据读取出错时
readerStream.on('error', function(err){
   console.log(err.stack);
});

再看数据输出

var fs = require("fs");

var readerStream = fs.createReadStream('video.mp4');
var writerStream = fs.createWriteStream('video-bk.mp4');

readerStream.on('data', function(chunk) {
   writerStream.write(chunk);
});

pipe直接对接输入输出流

上面的输出代码可以通过pipe简化为:

var fs = require("fs");

var readerStream = fs.createReadStream('video.mp4');
var writerStream = fs.createWriteStream('video-bk.mp4');

readerStream.pipe(writerStream);

4. http和https模块

4.1 创建http服务器。

//server.js
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
//在cmd下运行node server.js即可。

4.2 爬取网页内容

http继承于stream类,所以其监听事件与stream很像。
如果是http网站。

const http = require('http');
http.get("http://www.royotech.com",function(response){
    var html = "";
    response.on("data", function(data){
        html = html + data;
    });
    response.on("end", function(data){
        console.log("网页爬取结束");
        console.log(html); 
    });
});

如果是https网站。

const http = require('http');
http.get("https://www.sina.com",function(response){
    var html = "";
    response.on("data", function(data){
        html = html + data;
    });
    response.on("end", function(data){
        console.log("网页爬取结束");
        console.log(html); 
    });
});

5. cheerio模块

该模块可以用来分析html代码。

const http = require('http');
const cheerio = require('cheerio');

http.get("http://www.royotech.com",function(response){
    var html = "";
    response.on("data", function(data){
        html = html + data;
    });
    response.on("end", function(data){
        console.log("网页爬取结束");
        const $ = cheerio.load(html);
        let linksSet = $('a');
        for(let i=0; i<linksSet.length; i++){
            let url = linksSet[i].attribs.href;
            if(url && url.search("http") != -1)
                console.log(url);
        }
    });
});

6. express框架

express框架本质上是基于Node.js的web相关模块进行的封装。

6.1 使用express框架搭建一个web服务器。

const express = require("express");
const app = express();
app.listen(3030,function(err){
    if(err){
        console.log(err);
        return;
    } else {
        console.log("服务器已经启动,请访问http://localhost:3030");
    }
});
app.get("/",function(request,response){
    response.send("这里是首页");
 });

cmd下运行node index.js后访问:http://localhost:3030/

6.2 express的get方法。

const express = require("express");
const app = express();
app.listen(3030,function(err){
    if(err){
        console.log(err);
        return;
    } else {
        console.log("服务器已经启动,请访问http://localhost:3030");
    }
});
app.get("/",function(request,response){
    response.send(`这里是首页!<br/>
    你可以<a href="http://localhost:3030/get_username/">点我</a>进入get_username页面,或者直接在浏览器上输入"http://localhost:3030/get_username/"访问
    `);
 });
app.get("/get_username",function(request,response){
   response.send({
       name:"张三",
       sex:"男"
   });
});

cmd下运行node index.js后访问:http://localhost:3030/

6.3 express的post方法。

//根目录下index.js文件
const express = require("express");
const app = express();
app.listen(3030,function(err){
    if(err){
        console.log(err);
        return;
    } else {
        console.log("服务器已经启动,请访问http://localhost:3030");
    }
});

const bodyParser = require("body-parser");
app.use(bodyParser.json());//分析提交上来的数据,将数据转换为json后挂在到request的body属性上。
// 创建 application/x-www-form-urlencoded 编码解析
var urlencodedParser = bodyParser.urlencoded({ extended: false });
app.post("/doRegister", urlencodedParser, function(request,response){
    response.send({
        username:request.body.username,
        password:request.body.password
    });
});

根目录下register.html文件的内容如下:

<html>
    <head>
        <title>注册页面</title>
    </head>
    <body>
        <div>
            <form action="/doRegister" method="post">
                用户名:<input name="username" type="text">
                密码:<input name="password" type="password">
                <input type="submit" value="注册提交">
            </form>
        </div>
    </body>
</html>

6.4 使用express.static来设置静态文件。

加入我们在项目根目录中建立一个public文件夹,里面增加两个静态文件:

  • admin.html
  • flower.png
    admin.html的内容如下:

    <html>
    <head>
        <title>Hello Index</title>
    </head>
    <body>
        你好!绝对路径下的花朵
        <img src="/flower.png">
        <hr/>
        你好!相对路径下的花朵
        <img src="./flower.png">
    </body>
    </html>

    再在根目录创建index.js文件,代码如下:

    const express = require("express");
    const app = express();
    app.listen(3030,function(err){
    if(err){
        console.log(err);
        return;
    } else {
        console.log("服务器已经启动,请访问http://localhost:3030");
    }
    });
    app.use(express.static(__dirname + "/public"));

    cmd下运行node index.js后,浏览器中访问http://localhost:3030/admin.html 的结果如下:
    file

这里要表达的是:
当某个文件夹A使用express.static设置为静态文件存储目录后,无论之前文件夹A所处的实际目录在什么位置,被设置后的A文件夹都被视为网站的根目录,对应的A文件夹下面的所有文件都被视为在网站根目录下,且文件类型都是静态文件。

6.5 配合axios实现Ajax功能。

//index.js
const express = require("express");
const app = express();
app.listen(3030,function(err){
    if(err){
        console.log(err);
        return;
    } else {
        console.log("服务器已经启动,请访问http://localhost:3030");
    }
});
app.use(express.static(__dirname + "/public"));

app.get("/get_username",function(request,response){
   response.send({
       name:"张三",
       sex:"男"
   });
});
<!--index.html-->
<html>
    <head>
        <title>Hello Index</title>
        <script src="https://cdn.bootcss.com/axios/0.19.0/axios.min.js"></script>
        <script>
                function getUserName(){
                    axios.get("./get_username").then(function(result){
                        document.getElementById("user_name").innerHTML = result.data.name;
                    });
                }
        </script>
    </head>
    <body>
        这里是网站首页
        <button onClick="getUserName()">获取用户名,通过Ajax请求</button>
        <div id="user_name"></div>
    </body>
</html>

6.6 使用Express的Router路由

const express = require("express");
const app = express();
app.listen(3030,function(err){
    if(err){
        console.log(err);
        return;
    } else {
        console.log("服务器已经启动,请访问http://localhost:3030");
    }
});
app.use(express.static(__dirname + "/public"));

const userRouter = express.Router();
const genderRouter = express.Router();
userRouter.get("/",function(request,response){
    response.send({
        name:"张三",
    });
 });
 genderRouter.get("/",function(request,response){
    response.send({
        gender:"男",
    });
 });
app.use("/get_username",userRouter);
app.use("/get_gender",genderRouter);

// //等同于下面的代码
// app.get("/get_username",function(request,response){
//    response.send({
//        name:"张三",
//    });
// });

// app.get("/get_gender",function(request,response){
//     response.send({
//         gender:"男",
//     });
//  });

7. Node.js连接MongoDB数据库

7.1 安装mongodb模块。

cmd切换到工作目录,然后:

cnpm install mongodb --save

7.2 连接数据库代码

//数据库名:product
//集合名:product_info
const mongodb = require("mongodb");
const mongoClient = mongodb.MongoClient;
var DB_CONN_STR = "mongodb://localhost:27017";
mongoClient.connect(DB_CONN_STR,function(err,client){
    if(err){
        console.log(err);
    } else {
        console.log("数据库连接成功!");
    }
    client.db("product").collection("product_info").find({}).toArray(function(err,result){
        if(err){
            console.log(err);
        } else {
            console.log(result);
        }
    });
    client.close();
});

如何将Joomla1.5升级至Joomla2.5

前言

本文转自我之前的博客,写于2012年。以下为文章原文:

近期接了一个荷兰的订单,主要的工作是将当前网站的Joomla1.5系统升级为Joomla2.5系统。

在进行升级工作之前,我曾经查阅了很多互联网上的文献,遗憾的是,国内没有一篇完整记录Joomla1.5升级至2.5的技术类文章。想起来还是我的英语帮了我大忙,经过不断的搜索,从Joomla官方网站上找到了Joomla1.5升级为Joomla2.5的权威文章。文章链接如下:http://docs.joomla.org/Migrating_from_Joomla_1.5_to_Joomla_2.5

为了方便不习惯看英文文档的技术ers,我将文章的精髓进行翻译,并补充各种我在实际升级过程中遇到的问题。

Joomla1.5升级至Joomla2.5神功现在开始!

一、升级之前的准备。

升级开始前,以下准备工作是必须的。

  1. 重新判断一下,你的Joomla是否真的要升级?当然,如果你是接单赚钱的话,没有真不真的问题,你肯定要做的。但是如果是你自己的网站,你真的要考虑一下,是否真的有升级的必要?因为升级就肯定存在风险,而且产生很多附带的工作量,对于这两点,你必须做好思想准备。
  2. 你的Joomla1.5是不是最新版本?这里的最新是针对Joomla1.5这个大版本号而言的,Joomla1.5的最新版本是1.5.26。如果你的Joomla1.5不是最新版本,则必须要将Joomla1.5升级至最新版本后才可以进行后面的操作。(原因:我们将会使用JUpgrade进行Joomla的升级工作,而JUpgrade支持Joomla1.5.26到Joomla2.5的升级,很多朋友都抱怨JUpgrade不好用,其实是因为自己的Joomla1.5版本不对)。
    经验之谈:至于如何将Joomla1.5升级至最新版本,请看这篇文章:how to update from Joomla! 1.5.x to the latest version(这篇文章也是英文的,但是内容比较简单,如果大家有问题可以随时联系我,我抽时间会将此文章也翻译一下)。
  3. 当前的Joomla插件是否支持Joomla2.5?这也是个不大不小的问题。某些在Joomla1.5下面安装的插件在升级后是不支持Joomla2.5的。因此先要对插件的情况进行一下了解。最理想的情况是在升级Joomla之前,现在Joomla1.5的环境下将所有插件都升级到最新版本,之后再进行Joomla的升级。
    经验之谈:在升级过程中,插件方面我是比较幸运的。我没有按照要求对插件进行升级,而是先对Joomla进行了升级,升级后发现,诸如“Acymailing”,“Community Builder”等插件是无法正常使用的。但好在这两个插件都有相关的补救办法,具体方法我再后面还会说明。
  4. 你是否更改了Joomla的核心文件?有关“核心文件”的定义并没有确切的界限,简单的说,凡是升级过程中会被新文件覆盖的文件都属于核心文件。这个问题我想大家不要过于纠结,因为从1.5升级至2.5是大版本号的升级,很多核心文件都会被替换,所以如果你确实修改了很多核心文件的话,我建议你还是考虑一下升级的必要性。
  5. 你当前使用的Joomla模版和Joomla语言包是否支持Joomla2.5,这个插件的兼容性基本上属于一个问题。还是那句话,既然进行大版本号的升级,就要做好一切准备。

二、备份、备份、一定要备份

首先还是要说,我的升级之路比较顺利,虽然遇到过问题,但是没有遇到哪些需要重新来过的大问题。但是对于每一个即将进行升级操作的人而言,我还是想各位说一句“一定要备份、备份、在备份”。这里包括数据库和代码的完整备份,如果哪位连怎么备份都不知道的话,我建议您还是不要升级了,您肯定搞不定接下来的事情。

三、开始升级

1. 首先需要下载升级工具jUpgrade。

地址如下:http://extensions.joomla.org/search?q=jupgrade。话说这个工具真是个神作,这里要特别重复一下,这个工具是绝对好用的,如果你使用的时候有异常,多半情况下是没有按照本文第一部分的要求将Joomla1.5升级至最新版本

2. 安装jUpgrade。

安装方法就是Joomla1.5通用扩展安装方法。

2.1 进入你的Joomla1.5后台,然后依次点击Extensions >> Install/Uninstall。
file
2.2 Browse >> Select com_jupgrade >> Upload File & Install
file
2.3 安装成功
file

3. 启动“Mootools Upgrade ”插件。

  • 进入 Extensions | Plugin Manager
  • 查找 "System - Mootools Upgrade"
  • 将此插件开启
    请确保Mootools Upgrade插件已经正确安装并开启, jUpgrade需要依靠此插件才能正常运行。

4. 对JUpgrade进行设置。

截止到本文编写时,JUpgrade的版本号是2.5,支持将Joomla从1.5升级至1.7或者2.5,并附带了众多的升级选项设置。具体设置位置在:Administrator > Components > jUpgrade > Parameters.Global

  • Distribution - 选择要升级到的Joomla版本号,1.7或是2.5
  • Prefix for old database - 当前Joomla网站的数据库表前缀
  • Prefix for new database - 升级后的Joomla网站的数据库表前缀

Skips
Skip checks - 略过升级前的检测
Skip download - 略过下载升级包(注意:当你已经自行下载了升级包,并将升级包放入temp文件夹中,才选择此选项)
Skip decompress - 略过解压缩(注意:当你自行下载升级包后,且将升级包放入temp文件夹中,并自行解压缩后,才选择此选项)

Templates
Keep original positions - 保留原始的目录结构

Debug
Enable Debug - 允许错误调试,当升级出现错误的时候,显示相关的错误信息。

以上信息配置完成后,保存即可。

5. 移植开始。

依次进入:Components >> jUpgrade
file
开始升级
file
file
这个时候要注意不要退出页面,直至所有加载条都完成加载后才可以关闭页面。
file

另一个注意事项,JUpgrade只将Joomla1.5的默认模版进行了升级,因此当你打开升级后的网站时,网站的默认模版是被选中的,需要在后台进行切换才可以。

四、升级之后

  1. 首先先要恭喜你经过了这个惊心动魄的过程。话说我在进行升级的过程中,每点击一个按钮时心都提到嗓子眼了,当发现没有报错,又会暗自狂喜一下。
  2. JUpgrade的升级过程实际上是非常安全的。JUpgrade在升级网站的时候,没有对原来的Joomla网站进行任何的修改,原来的数据库表、源文件还是在那里,JUpgrade实际是在原来网站的根目录里面创建了一个“jupgrade”文件夹,并将升级后的Joomla2.5网站文件保存在了这个文件夹里面。
  3. 依次检查一下下面的这些功能是否还是正常工作:
    Banners
    Categories
    Contacts
    Content
    Menus
    Modules
    Newsfeeds
    Users
  4. 了解了升级原理,并确认升级成功后,下面就可以对原有网站进行清楚了。清楚地方法也很简单,将数据库中Joomla1.5表前缀的数据表全部清除,并将网站根目录中除了jugrade文件夹的源文件全部删除,然后将jugrade文件夹中的文件转移至网站根目录即可。

五、有关“Acymailing”和“Community Builder”升级后的问题。

这两个插件在Joomla从1.5升级至2.5都出现了问题。问题描述和解决办法如下:

1. Acymailing

问题描述:升级后Acymailing在Component菜单下直接消失了。
解决方法:很简单,下载一个最新版的Acymailing插件,然后重新安装即可。可以放心,原来的数据都会保留。

2. Community Builder

问题描述:升级后点击Community Builder菜单后报错。
解决方法:也不难,下载一个最新版的CB,解压缩后按照里面的提示对CB进行升级即可,原来的数据库也会保留。

彻底搞懂npm -g,npm -save,npm -save-dev

1. 回顾 npm install 命令

最近在写Node程序的时候,突然对 npm install 的-save和-save-dev 这两个参数的使用比较混乱。其实博主在这之前对这两个参数的理解也是模糊的,各种查资料和实践后对它们之间的异同点略有理解。遂写下这篇文章避免自己忘记,同时也给node猿友一点指引。

我们在使用 npm install 安装模块的模块的时候 ,一般会使用下面这几种命令形式:

npm install moduleName # 安装模块到项目目录下

npm install -g moduleName # -g 的意思是将模块安装到全局,具体安装到磁盘哪个位置,要看 npm config prefix 的位置。

npm install -save moduleName # -save 的意思是将模块安装到项目目录下,并在package.json文件的dependencies节点写入依赖。

npm install -save-dev moduleName # -save-dev 的意思是将模块安装到项目目录下,并在package.json文件的devDependencies节点写入依赖。

那么问题来了,在项目中我们应该使用四个命令中的哪个呢?这个就要视情况而定了。下面对这四个命令进行对比,看完后你就不再这么问了。

2. npm install moduleName 命令

  1. 安装模块到项目node_modules目录下。
  2. 不会将模块依赖写入package.json的devDependencies或dependencies节点。
  3. 运行 npm install 初始化项目时不会下载模块。

3. npm install -g moduleName 命令

  1. 安装模块到全局,不会在项目node_modules目录中保存模块包。
  2. 不会将模块依赖写入package.json的devDependencies或dependencies 节点。
  3. 运行 npm install 初始化项目时不会下载模块。

4. npm install -save moduleName 命令

  1. 安装模块到项目node_modules目录下。
  2. 将模块依赖写入package.json的dependencies节点。
  3. 运行 npm install 初始化项目时,会将模块下载到项目目录下。
  4. 运行npm install --production或者注明NODE_ENV变量值为production时,会自动下载模块到node_modules目录中。

5. npm install -save-dev moduleName 命令

  1. 安装模块到项目node_modules目录下。
  2. 将模块依赖写入package.json的devDependencies节点。
  3. 运行 npm install 初始化项目时,会将模块下载到项目目录下。
  4. 运行npm install --production或者注明NODE_ENV变量值为production时,不会自动下载模块到node_modules目录中。

6. 总结

devDependencies 节点下的模块是我们在开发时需要用的,比如项目中使用的 gulp ,压缩css、js的模块。这些模块在我们的项目部署后是不需要的,所以我们可以使用 -save-dev 的形式安装。像 express 这些模块是项目运行必备的,应该安装在 dependencies 节点下,所以我们应该使用 -save 的形式安装。

本文转自:https://www.limitcode.com/detail/59a15b1a69e95702e0780249.html