Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

根据业务场景动态换肤办法---从入口处考虑 #5

Open
ghost opened this issue May 24, 2017 · 0 comments
Open

根据业务场景动态换肤办法---从入口处考虑 #5

ghost opened this issue May 24, 2017 · 0 comments

Comments

@ghost
Copy link

ghost commented May 24, 2017

根据业务场景动态换肤办法

实现根绝业务场景,比如:不同渠道(pc,微信)等,动态整体换肤功能,主要有两种方法。

使用JS动态设置样式表(客户端换肤)

这种方法主要是在浏览器渲染过程中,根据参数(可能来自请求参数,或者浏览器嗅探)动态设置样式表。

主体代码,大致如下:

var getUrlParam = function(name) {
    var reg = new RegExp("(^|&|#|/?)" + name + "=([^&]*)(&|$)", "i");
    var r = window.location.href.substr(1).match(reg);
    if (r != null) {
        var result = unescape(r[2]);
        if (result.indexOf('#') > 0) {
            result = result.slice(0, result.indexOf('#'));
        };
        return result;
    }
    return null;
};
var type = getUrlParam('type');
function addCssByLink(url){
    var doc=document;
    var link=doc.createElement("link");
    link.setAttribute("rel", "stylesheet");
    link.setAttribute("type", "text/css");
    link.setAttribute("href", url);

    var heads = doc.getElementsByTagName("head");
    if(heads.length)
        heads[0].appendChild(link);
    else
        doc.documentElement.appendChild(link);
}
//方法一:动态生成link标签
//        addCssByLink('css/' + type + '.css');

function setStyleSheet(url) {
    var doc=document;
    var link = doc.getElementById('css');
    if (link){
        link.setAttribute("href", url);
    }else {
        addCssByLink(url);
    }
}
//方法二:动态设置href的值。
setStyleSheet('css/' + type + '.css')

这种方法,确确实实实现了根据不同参数换肤的功能。但是,这种方法会存在闪烁问题。

闪烁问题,主要是因为再次请求、重绘和大量图片等问题造成的。

而且,对于多页应用(非SPA)需要在每一个页面中都执行这段代码,以确保每一个页面都正确显示。

服务端动态设置样式表

这种方式,就是在服务端返回页面时,动态设置html中link标签的属性。

比如,常见的JSP页面可以这样写:

<link type="text/css" rel="stylesheet" href="assets/css/<%=request.getParameter('type')%>.css" />

但是,现在谁还写JSP页面,所以,这种方式忽略。

另外一种方式,就是服务端渲染。这里有几个关键字:单页面应用,服务端渲染,动态生成link,Node中间层。

  • SPA(单页面应用):首先,我们的页面应该设置成单页形式,通过router机制实现页面切换。这种方式有个好处,我们只有一个主页面,所以只需要设置一次link。
  • 服务端渲染:服务端渲染一般都是用来提高首屏加载速度。服务端渲染,就是在服务端返回数据之前,生成一个已经完全装填完毕的页面。在此处,我们正好可以利用这一特点,在返回数据之前,根据参数,动态替换link属性,从而规避在客户端执行JS代码来造成的闪烁问题。
  • 动态生成link:这个在第二点已经介绍了。
  • Node中间层:服务端渲染首先需要服务器具备服务端渲染的能力。在此处,我们可以使用NodeJS。首先,Node对于高并发有天然的优势;其次,独立出一个Node层,有利于前后端分离,让Node来做静态服务管理和首屏渲染等内容。具体架构参考
    http://frontenddev.org/link/full-stack-development-with-nodejs-1.html#heading-2-9http://zhenhua-lee.github.io/react/goku.html

服务端渲染的具体实现方式可以参考如下代码:

'use strict'

var fs = require('fs')
var path = require('path')
var url=require("url");
var querystring=require("querystring");

// Define global Vue for server-side app.js
global.Vue = require('vue')

// Get the HTML layout
var layout = '<!DOCTYPE html>\
    <html>\
    <head>\
    <title>My Vue App</title>\
    <link href="#" rel="stylesheet" type="text/css" />\
    <script src="/assets/vue.js"></script>\
    </head>\
    <body>\
    <div class="app" id="app"></div>\
    <div class="app">test</div>\
    <div class="app">test</div>\
    <div class="app">test</div>\
    <div class="app">test</div>\
    <div class="app">test</div>\
    <div class="app">test</div>\
    <div class="app">test</div>\
    <div class="app">test</div>\
    <div class="app">test</div>\
    <script src="/assets/app.js"></script>\
    <script>app.$mount(\'#app\')</script>\
    </body>\
    </html>'

// Create a renderer
var renderer = require('vue-server-renderer').createRenderer()

// Create an express server
var express = require('express')
var server = express()

// Serve files from the assets directory
server.use('/assets', express.static(
    path.resolve(__dirname, 'assets')
))

// Handle all GET requests
server.get('/assets/index', function (request, response) {
    // Render our Vue app to a string
    renderer.renderToString(
        // Create an app instance
        require('./assets/app')(),
        // Handle the rendered result
        function (error, html) {
            // If an error occurred while rendering...
            if (error) {
                // Log the error in the console
                console.error(error)
                // Tell the client something went wrong
                return response
                        .status(500)
                        .send('Server Error')
            }
            var str=url.parse(request.url,true).query;
            var arg = querystring.parse(url.parse(request.url).query);
            var type = arg.type || 'classic';


            //这里根据参数等信息动态替换link
            var data = layout.replace('<div id="app"></div>', html)
                .replace('<link href="#" rel="stylesheet" type="text/css" />','<link href="/assets/css/'+ type +'.css" rel="stylesheet" type="text/css" />');

            // Send the layout with the rendered app's HTML
            response.send(data)
        }
    )
})

// Listen on port 5000
server.listen(5000, function (error) {
  if (error) throw error
  console.log('Server is running at localhost:5000')
})

关于以上两个方式,我均做了一个简单的Demo,分别可以体验客户端JS替换和服务端替换的差别。具体源代码请联系我。

另外:对于通过PhoneGap或者Cordoba打包的H5应用,虽然,css文件和图片等已经被打包到本地,但是在替换CSS时,仍然会有轻微的闪烁问题。这种方式,是属于客户端替换,本人已亲自体验过这种换肤方式。

对于JS动态替换,折中的实现方式是,另外一套样式尽量不大面积改变DOM结构,对于背景图片和icon等内容,应进行较少量的变化。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

0 participants