精华内容
下载资源
问答
  • 踩了很长时间的坑,总算是...在服务端渲染图表,绕不开的一个问题就是,没有DOM怎么绘图?这个主要有两种解决方案,一个是用那些headless的浏览器去渲染,然后进行截图;另一个就是在Node环境下模拟DOM元素,比如我...

    踩了很长时间的坑,总算是能跑起来了。但是如果要我给echarts的SSR一个评价,那就是不好用……可能是我太菜了。而且,因为我是Windows用户,这个过程对Windows极其不友好。友情提示:入坑请慎重。


    在服务端渲染图表,绕不开的一个问题就是,没有DOM怎么绘图?这个主要有两种解决方案,一个是用那些headless的浏览器去渲染,然后进行截图;另一个就是在Node环境下模拟DOM元素,比如我在这里想用canvas,就得装个node-canvas;如果想用SVG,就得用JSDOM一类的库。我这里主要是用的canvas,所以就用node-canvas了。

    首先,需要安装node-canvas和echarts。echarts不用多说,但是有一点,不建议使用官方推荐的node-echarts,版本陈旧,而且依赖的库新版本的Node(12.x)不支持。

    node-canvas安装请参考官方文档,因为安装流程比较复杂,尤其是Windows用户:https://github.com/Automattic/node-canvas/wiki/Installation:-Windows。

    当然,因为众所周知的原因,最后一步用npm install安装node-canvas是个问题,因为它预编译的安装包好像是在AWS上,大概率会卡在这里:

    node-pre-gyp WARN Using request for node-pre-gyp https download
    

    或者,还有可能卡在这里:

    node-pre-gyp info install unpacking Release/
    node-pre-gyp info install unpacking Release/canvas.exp
    node-pre-gyp info install unpacking Release/canvas.ilk
    

    然后还有可能直接报错。解决方案就是,加个–build-from-source,不用他编译好的,而是在我们本机进行编译:

    npm install canvas --verbose --build-from-source
    

    在这个过程中,还有可能报错,比较常见的有这些:

    1. 找不到node-gyp。

      解决办法npm install -g node-gyp,装一个就是了。不过一般来说会自带才对……

    2. fatal error C1083: 无法打开包括文件: “cairo.h”: No such file or directory。
      

      解决方法:参考官方文档安装GTK 2,并放在合适的路径。

    3. fatal error C1083: 无法打开包括文件: “jpeglib.h”: No such file or directory
      

      解决方法:参考官方文档安装libjpeg-turbo,并放在合适的路径。

      注意,对于Windows用户,一定要安装for VC++的版本,不要装成for gcc的版本。

    4. Error: Cannot find module '../build/Release/canvas.node'
      

      解决方法:进入node_modules/canvas目录,然后使用node-gyp configure build手动编译。

    5. gyp: binding.gyp not found 
      

      解决方法:同4。还有一种可能是缺少windows-build-tools,这个在第7点中说。

    6. node.lib已损坏。
      

      解决方法:升级Node版本,或者尝试Node安装包的repair功能。大概率是Node没装好,或者你使用了canvas-prebuilt这个已经废弃的库。实测升级到最新版本12.16.1可以修复。

    7. 与windows-build-tools相关的一系列错误。

      解决方法:npm install --global --production windows-build-tools

      但是在此过程中可能会出现一系列问题。可能会卡在这里:

      Python 2.7.16 is already installed, not installing again.
      

      也有可能卡在这里:

      Successfully installed Python 2.7
      

      或者提示安装完了,但一直没有退出,请往下看。

      第一次卡住的时候,尝试重复一遍install,如果解决了那自然最好,如果没解决,甚至报错:

      Error: EBUSY: resource busy or locked
      

      那么我们可能是遇到了同样的问题。网上有两种方案,我放在这里做个参考,虽然对我来说都没用就是了:

      1. 先安装一个旧版本npm install --global --production windows-build-tools@4.0.0,然后重新npm install -g --production windows-build-tools,就可以了。

      2. 针对resource busy or locked的报错,先在任务管理器里kill掉BuildTolls_Full.exe这个进程,然后去C:\Users\<你的用户名>\.windows-build-tools里找到build-tools-log.txt,在这个文件的最后增加一行:

        Variable: IsInstalled = 1
        

        保存后重新install。

      但是这两种对我来说都没用。后来,我无意中看到一个方法,死马当活马医,居然成了……说起来也很迷幻,加个–verbose就好了:

      npm install --global --production --verbose windows-build-tools
      

    到了这里,基本上就可以开始了。其实方法并不神秘,主要就是这么一个方法:

    const path = require('path')
    const { createCanvas } = require('canvas')
    const echarts = require('echarts')
    
    function generateImage(options) {
      const canvas = createCanvas(600, 600) // 600 * 600的canvas
      const ctx = canvas.getContext('2d')
      ctx.font = '12px'
      echarts.setCanvasCreator(() => canvas) // 使用node-canvas
      const chart = echarts.init(canvas)
      options.animation = false
      options.textStyle = {
        fontSize: 12
      }
      chart.setOption(options) // 就是echarts的options
      return chart.getDom().toBuffer() // 返回buffer
    }
    

    这里也可以用fs.writeFileSync来进行文件读写,不过我更倾向于返回buffer流,个人爱好而已。不过,返回buffer流的话,前端需要一些处理,以axios为例,需要设置responseType为blob,然后用createObjectURL来处理blob,然后把url放到img里去:

    const { data } = await axios.get('/api/chart', {
        responseType: 'blob'
    })
    
    URL.createObjectURL(data) // 放进img.src的url
    

    最终效果差不多是这样:
    在这里插入图片描述
    同时,服务端渲染还面临着服务端不支持中文导致乱码,图片不清晰等问题,这些我还没有特别好的处理办法,只能看情况进行处理。我只说说我试过有用的办法:

    1. 针对乱码问题,node-canvas 2.x提供了一个导入字体的方法registerFont(),可以指定中文字体。但是我并不喜欢这个方法,平白无故增加静态资源的数量。

      据说在服务器上装好中文字体可以解决,但在我这里没用。

    2. 图片不清晰,可以在init的时候增大像素比:

      echarts.init(canvas, null, {devicePixelRatio: 2});
      

      但是这又有一个问题,这么弄出来的图片大小会翻倍。本来我用SSR就是希望提高性能,为了清晰度还得牺牲性能,就有点本末倒置了。

    总的来说,目前我还没有发现对echarts进行SSR的好处,可能用那些更轻量的图表库配合SVG效果会比较好,比如D3。可能有用的场景就是显示那些对清晰度要求不太高的图表,比如图表的动态缩略图,因为有时候可能会有这样的需求,虽然是缩略图或者是示意图,也希望能够动态更新,因为前后数据变化可能比较大。

    参考资料

    https://github.com/Automattic/node-canvas/issues/1468#issuecomment-522961098

    https://github.com/mapbox/node-pre-gyp/issues/477#issuecomment-534231739

    展开全文
  • 首先nodejs环境,为了切换nodejs版本方便,安装nvm,参考此文:使用nvm安装nodejs因为nodejs的版本对相关module库的兼容性,是非常相关的。

    首先nodejs环境,为了切换nodejs版本方便,安装nvm,参考此文: 使用nvm安装nodejs因为nodejs的版本对相关module库的兼容性,是非常相关的。

    展开全文
  • 转载自:http://www.jianshu.com/p/0ecd727107bb# Github地址: ... 目录 前言服务端渲染好处思考原理同构方案状态管理方案路由方案静态资源处理方案动态加载方案优化方案部署方案其它结尾 前言 前段时

    转载自:http://www.jianshu.com/p/0ecd727107bb#

    Github地址: https://github.com/chikara-chan/react-isomorphic-boilerplate

    目录

    • 前言
    • 服务端渲染好处
    • 思考
    • 原理
    • 同构方案
    • 状态管理方案
    • 路由方案
    • 静态资源处理方案
    • 动态加载方案
    • 优化方案
    • 部署方案
    • 其它
    • 结尾

    前言

    前段时间公司有一个产品需求要求使用Node.js中间层来做服务端渲染,于是翻遍了整个技术社区,没有找到一个特别合适的脚手架,作为一个有追求的前端攻城狮,决定自己去搭建一套最完美的服务端渲染开发环境,期间踩过无数的坑,前前后后差不多折腾了三周时间。

    服务端渲染好处

    1. SEO,让搜索引擎更容易读取页面内容
    2. 首屏渲染速度更快(重点),无需等待js文件下载执行的过程
    3. 更易于维护,服务端和客户端可以共享某些代码

    思考

    1. 如何实现组件同构?
    2. 如何保持前后端应用状态一致?
    3. 如何解决前后端路由匹配问题?
    4. 如何处理服务端对静态资源的依赖?
    5. 如何配置两套不同的环境(开发环境和产品环境)?
    6. 如何划分更合理的项目目录结构?

    由于服务端渲染配置的复杂性,大部分人望而止步,而本文的目的就在于教你如何搭建一套优雅的服务端渲染开发环境,从开发打包部署优化到上线。

    原理


    一个服务端渲染的同构web应用架构图大致如上图所示,得力于Node.js的发展与流行,Javascript成为了一门同构语言,这意味着我们只需写一套代码,可以同时在客户端与服务端执行。

    同构方案

    这里我们采用React技术体系做同构,由于React本身的设计特点,它是以Virtual DOM的形式保存在内存中,这是服务端渲染的前提。

    对于客户端,通过调用ReactDOM.render方法把Virtual DOM转换成真实DOM最后渲染到界面。

    import { render } from 'react-dom'
    import App from './App'
    
    render(<App />, document.getElementById('root'))

    对于服务端,通过调用ReactDOMServer.renderToString方法把Virtual DOM转换成HTML字符串返回给客户端,从而达到服务端渲染的目的。

    import { renderToString } from 'react-dom/server'
    import App from './App'
    
    async function(ctx) {
        await ctx.render('index', {
            root: renderToString(<App />)
        })
    }

    状态管理方案

    我们选择Redux来管理React组件的非私有组件状态,并配合社区中强大的中间件Devtools、Thunk、Promise等等来扩充应用。当进行服务端渲染时,创建store实例后,还必须把初始状态回传给客户端,客户端拿到初始状态后把它作为预加载状态来创建store实例,否则,客户端上生成的markup与服务端生成的markup不匹配,客户端将不得不再次加载数据,造成没必要的性能消耗。

    服务端
    import { renderToString } from 'react-dom/server'
    import { Provider } from 'react-redux'
    import { createStore } from 'redux'
    import App from './App'
    import rootReducer from './reducers'
    
    const store = createStore(rootReducer)
    
    async function(ctx) {
        await ctx.render('index', {
            root: renderToString(
                <Provider store={store}>
                    <App />
                </Provider>
            ),
            state: store.getState()
        })
    }
    HTML
    <body>
        <div id="root"><%- root %></div>
        <script>
            window.REDUX_STATE = <%- JSON.stringify(state) %>
        </script>
    </body>
    客户端
    import { render } from 'react-dom'
    import { Provider } from 'react-redux'
    import { createStore } from 'redux'
    import App from './App'
    import rootReducer from './reducers'
    
    const store = createStore(rootReducer, window.REDUX_STATE)
    
    render(
        <Provider store={store}>
            <App />
        </Provider>, 
        document.getElementById('root')
    )

    路由方案

    客户端路由的好处就不必多说了,客户端可以不依赖服务端,根据hash方式或者调用history API,不同的URL渲染不同的视图,实现无缝的页面切换,用户体验极佳。但服务端渲染不同的地方在于,在渲染之前,必须根据URL正确找到相匹配的组件返回给客户端。
    React Router为服务端渲染提供了两个API:

    • match 在渲染之前根据URL匹配路由组件
    • RoutingContext 以同步的方式渲染路由组件
    服务端
    import { renderToString } from 'react-dom/server'
    import { Provider } from 'react-redux'
    import { createStore } from 'redux'
    import { match, RouterContext } from 'react-router'
    import rootReducer from './reducers'
    import routes from './routes'
    
    const store = createStore(rootReducer)
    
    async function clientRoute(ctx, next) {
        let _renderProps
    
        match({routes, location: ctx.url}, (error, redirectLocation, renderProps) => {
            _renderProps = renderProps
        })
    
        if (_renderProps) {
            await ctx.render('index', {
                root: renderToString(
                    <Provider store={store}>
                        <RouterContext {..._renderProps} />
                    </Provider>
                ),
                state: store.getState()
            })
        } else {
            await next()
        }
    }
    客户端
    import { Route, IndexRoute } from 'react-router'
    import Common from './Common'
    import Home from './Home'
    import Explore from './Explore'
    import About from './About'
    
    const routes = (
        <Route path="/" component={Common}>
            <IndexRoute component={Home} />
            <Route path="explore" component={Explore} />
            <Route path="about" component={About} />
        </Route>
    )
    
    export default routes

    静态资源处理方案

    在客户端中,我们使用了大量的ES6/7语法,jsx语法,css资源,图片资源,最终通过webpack配合各种loader打包成一个文件最后运行在浏览器环境中。但是在服务端,不支持import、jsx这种语法,并且无法识别对css、image资源后缀的模块引用,那么要怎么处理这些静态资源呢?我们需要借助相关的工具、插件来使得Node.js解析器能够加载并执行这类代码,下面分别为开发环境和产品环境配置两套不同的解决方案。

    开发环境
    1. 首先引入babel-polyfill这个库来提供regenerator运行时和core-js来模拟全功能ES6环境。
    2. 引入babel-register,这是一个require钩子,会自动对require命令所加载的js文件进行实时转码,需要注意的是,这个库只适用于开发环境。
    3. 引入css-modules-require-hook,同样是钩子,只针对样式文件,由于我们采用的是CSS Modules方案,并且使用SASS来书写代码,所以需要node-sass这个前置编译器来识别扩展名为.scss的文件,当然你也可以采用LESS的方式,通过这个钩子,自动提取className哈希字符注入到服务端的React组件中。
    4. 引入asset-require-hook,来识别图片资源,对小于8K的图片转换成base64字符串,大于8k的图片转换成路径引用。
    // Provide custom regenerator runtime and core-js
    require('babel-polyfill')
    
    // Javascript required hook
    require('babel-register')({presets: ['es2015', 'react', 'stage-0']})
    
    // Css required hook
    require('css-modules-require-hook')({
        extensions: ['.scss'],
        preprocessCss: (data, filename) =>
            require('node-sass').renderSync({
                data,
                file: filename
            }).css,
        camelCase: true,
        generateScopedName: '[name]__[local]__[hash:base64:8]'
    })
    
    // Image required hook
    require('asset-require-hook')({
        extensions: ['jpg', 'png', 'gif', 'webp'],
        limit: 8000
    })
    产品环境

    对于产品环境,我们的做法是使用webpack分别对客户端和服务端代码进行打包。客户端代码打包这里不多说,对于服务端代码,需要指定运行环境为node,并且提供polyfill,设置__filename和__dirname为true,由于是采用CSS Modules,服务端只需获取className,而无需加载样式代码,所以要使用css-loader/locals替代css-loader加载样式文件

    // webpack.config.js
    {
        target: 'node',
        node: {
            __filename: true,
            __dirname: true
        },
        module: {
            loaders: [{
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel',
                query: {presets: ['es2015', 'react', 'stage-0']}
            }, {
                test: /\.scss$/,
                loaders: [
                    'css/locals?modules&camelCase&importLoaders=1&localIdentName=[hash:base64:8]',
                    'sass'
                ]
            }, {
                test: /\.(jpg|png|gif|webp)$/,
                loader: 'url?limit=8000'
            }]
        }
    }

    动态加载方案

    对于大型Web应用程序来说,将所有代码打包成一个文件不是一种优雅的做法,特别是对于单页面应用,用户有时候并不想得到其余路由模块的内容,加载全部模块内容,不仅增加用户等待时间,而且会增加服务器负荷。Webpack提供一个功能可以拆分模块,每一个模块称为chunk,这个功能叫做Code Splitting。你可以在你的代码库中定义分割点,调用require.ensure,实现按需加载,而对于服务端渲染,require.ensure是不存在的,因此需要判断运行环境,提供钩子函数。

    重构后的路由模块为

    // Hook for server
    if (typeof require.ensure !== 'function') {
        require.ensure = function(dependencies, callback) {
            callback(require)
        }
    }
    
    const routes = {
        childRoutes: [{
            path: '/',
            component: require('./common/containers/Root').default,
            indexRoute: {
                getComponent(nextState, callback) {
                    require.ensure([], require => {
                        callback(null, require('./home/containers/App').default)
                    }, 'home')
                }
            },
            childRoutes: [{
                path: 'explore',
                getComponent(nextState, callback) {
                    require.ensure([], require => {
                        callback(null, require('./explore/containers/App').default)
                    }, 'explore')
                }
            }, {
                path: 'about',
                getComponent(nextState, callback) {
                    require.ensure([], require => {
                        callback(null, require('./about/containers/App').default)
                    }, 'about')
                }
            }]
        }]
    }
    
    export default routes

    优化方案

    提取第三方库,命名为vendor

    vendor: ['react', 'react-dom', 'redux', 'react-redux']

    所有js模块以chunkhash方式命名

    output: {
        filename: '[name].[chunkhash:8].js',
        chunkFilename: 'chunk.[name].[chunkhash:8].js',
    }

    提取公共模块,manifest文件起过渡作用

    new webpack.optimize.CommonsChunkPlugin({
        names: ['vendor', 'manifest'],
        filename: '[name].[chunkhash:8].js'
    })

    提取css文件,以contenthash方式命名

    new ExtractTextPlugin('[name].[contenthash:8].css')

    模块排序、去重、压缩

    new webpack.optimize.OccurrenceOrderPlugin(), // webpack2 已移除
    new webpack.optimize.DedupePlugin(), // webpack2 已移除
    new webpack.optimize.UglifyJsPlugin({
        compress: {warnings: false},
        comments: false
    })

    使用babel-plugin-transform-runtime取代babel-polyfill,可节省大量文件体积
    需要注意的是,你不能使用最新的内置实例方法,例如数组的includes方法

    {
        presets: ['es2015', 'react', 'stage-0'],
        plugins: ['transform-runtime']
    }

    最终打包结果


    Paste_Image.png

    部署方案

    对于客户端代码,将全部的静态资源上传至CDN服务器
    对于服务端代码,则采用pm2部署,这是一个带有负载均衡功能的Node应用的进程管理器,支持监控、日志、0秒重载,并可以根据有效CPU数目以cluster的方式启动最大进程数目

    pm2 start ./server.js -i 0

    Paste_Image.png

    其它

    提升开发体验

    对于客户端代码,可以使用Hot Module Replacement技术,并配合koa-webpack-dev-middleware,koa-webpack-hot-middleware两个中间件,与传统的BrowserSync不同的是,它可以使我们不用通过刷新浏览器的方式,让js和css改动实时更新反馈至浏览器界面中。

    app.use(convert(devMiddleware(compiler, {
        noInfo: true,
        publicPath: config.output.publicPath
    })))
    app.use(convert(hotMiddleware(compiler)))

    对于服务端代码,则使用nodemon监听代码改动,来自动重启node服务器,相比supervisor,更加灵活轻量,内存占用更少,可配置性更高。

    nodemon ./server.js --watch server

    对于React组件状态管理,使用Redux DevTools这个中间件,它可以跟踪每一个状态和action,监控数据流,由于采用纯函数的编程思想,还具备状态回溯的能力。需要注意的是,React组件在服务端生命周期只执行到componentWillMount,因此要把该中间件挂载到componentDidMount方法上,避免在服务端渲染而报错。

    class Root extends Component {
        constructor() {
            super()
            this.state = {isMounted: false}
        }
        componentDidMount() {
            this.setState({isMounted: true})
        }
        render() {
            const {isMounted} = this.state
            return (
                <div>
                    {isMounted && <DevTools/>}
                </div>
            )
        }
    }

    代码风格约束

    推荐使用时下最为流行的ESLint,相比其它QA工具,拥有更多,更灵活,更容易扩展的配置,无论是对个人还是团队协作,引入代码风格检查工具,百益而无一害,建议你花个一天时间尝试一遍ESLint每一项配置,再决定需要哪些配置,舍弃哪些配置,而不是直接去使用Airbnb规范,Google规范等等。

    Tips: 使用fix参数可快速修复一些常见错误,在某种程度上,可以取代编辑器格式化工具

    eslint test.js --fix

    开发环境演示

    Youtubee视频,自备梯子
    https://www.youtube.com/watch?v=h3n3-v81PqY

    结尾

    时至今日,开源社区中并没有一个完美的服务端渲染解决方案,而当初搭建这个脚手架的目的就是从易用性出发,以最清晰的配置,用最流行的栈,组最合理的目录结构,给开发者带来最完美的开发体验,从开发打包部署优化到上线,一气呵成。即使你毫无经验,也可轻松入门服务端渲染开发。

    附上源码: https://github.com/chikara-chan/react-isomorphic-boilerplate

    学习前端的过程中,我整理了很多资料,也希望能共享出来帮助到更多刚接触或者接触前端不久的同学。不过也为了把控微信群的质量,入群的一定要是前端的小伙伴才可以。入群我就会把资料发给每个人,每天也会挑选前沿的前端高质量文章发到群里给大家学习。想加入的同学可以加 笑笑微信:iamaixiaoxiao,拉你入群。再次强调,保证群高质量,群非前端不加,请谅解哦。扫描微信二维码也可以。



    展开全文
  • 前言我们都知道, Vue和React是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出自定义组件,进行生成 DOM 和操作 DOM, 也就是我们常说的客户端渲染, 并且我们大部分...

    前言

    我们都知道, VueReact是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出自定义组件,进行生成 DOM 和操作 DOM, 也就是我们常说的客户端渲染, 并且我们大部分主流的场景都是SPA(单页面)应用, 而随着 SPA尤其是 ReactVueAngular 为代表的前端框架的流行,越来越多的 Web App 使用的是客户端渲染。

    使用客户端渲染的优势在于节省后端资源局部刷新前后端分离等,但随着应用的日益复杂, 首屏渲染时间不断变长, 并且存在严重的SEO问题。

    所以为了解决SPA应用遇到的这些问题, 我们必须考虑SSR:

    服务端渲染(ssr),是指由服务器端完成页面的HTML 结构拼接,并且直接将拼接好的HTML发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的处理技术。

    对于服务端渲染的页面,服务端可以直接将带数据的内容通过 HTML 文本的形式返回,搜索引擎爬虫可以轻易的获取页面内容,而对于客户端渲染的应用,客户端必须执行服务器返回的 Javascript 才能得到正确的网页内容。目前,除 GoogleBing 支持 Javascript 外(也会有一些限制),其他的大部分搜索引擎都不支持 Javascript,也就无法获取正确的网页内容。而本文要讲的技术方案,正是为了解决SPA下的SSR技术困境.接下来我们看看常用的ssr技术实现方案.

    摘要

    ssr(服务端渲染)技术实现方案

    接下来笔者将列举几个常用的基于vue/react的服务端渲染方案,如下:

    • 使用next.js/nuxt.js的服务端渲染方案

    • 使用node+vue-server-renderer实现vue项目的服务端渲染

    • 使用node+React renderToStaticMarkup实现react项目的服务端渲染

    • 传统网站通过模板引擎来实现ssr(比如ejs, jade, pug等)

    • 使用rendertron实现SPA项目的服务端渲染

    以上是笔者之前实践过的方案, 最后一种方案笔者将在下面一节详细介绍, 因为next/nuxt是已有的服务端渲染解决方案,文档写的比较详细,这里笔者就不再做过多介绍了,这里我们简单介绍一下第二种和第三种方案.

    1.使用node+vue-server-renderer实现vue项目的服务端渲染

    首先vue-server-renderer依赖node的api,所以只能运行在node环境, 我们需要先安装它:

    npm install vue vue-server-renderer --save
    

    在node中使用,代码如下:

    
    const Vue = require('vue')const server = require('express')()const renderer = require('vue-server-renderer').createRenderer()
    server.get('*', (req, res) => {  const app = new Vue({    data: {      url: req.url    },    template: `<div>趣谈前端:{{ url }}</div>`  })
      renderer.renderToString(app, (err, html) => {    if (err) {      res.status(500).end('Internal Server Error')      return    }    res.end(`      <!DOCTYPE html>      <html lang="en">        <head><title>Hello</title></head>        <body>${html}</body>      </html>    `)  })})
    server.listen(8080)
    

    当然实际情况比上面的案例要复杂很多, 我们可以专门写一个template.html,然后通过模板差值的方式导入后端数据,进而实现服务端渲染. 在使用这种方式的时候我们仍然要维护两套代码.

    2.使用node+React renderToStaticMarkup实现react项目的服务端渲染

    使用这种方案和vue的方案类似, 只不过这里我们用了react自带的api来实现ssr,简单的实现代码如下:

    
    var express = require('express');var app = express(); var React = require('react'),    ReactDOMServer = require('react-dom/server'); var App = React.createFactory(require('./App')); app.get('/', function(req, res) {    var html = ReactDOMServer.renderToStaticMarkup(        React.DOM.body(            null,            React.DOM.div({id: 'root',                dangerouslySetInnerHTML: {                    __html: ReactDOMServer.renderToStaticMarkup(App())                }            })        )    );     res.end(html);}); app.listen(80, function() {    console.log('running on port ' + 80);});
    

    以上使用了renderToStaticMarkup, 我们都知道react-dom提供了两种服务端渲染函数,如下:

    1. renderToString:将 React Component 转化为 HTML 字符串,生成的 HTML 的 DOM 会带有额外属性:各个 DOM 会有data-react-id属性,第一个 DOM 会有data-checksum属性。

    2. renderToStaticMarkup:将 React Component 转化为 HTML 字符串,但是生成 HTML 的 DOM 不会有额外属性,从而节省 HTML 字符串的大小。

    所以这里我们一般使用renderToStaticMarkup函数. 同理在实际业务场景中我们也会写2套代码来实现ssr.

    使用谷歌rendertron实现服务端渲染

    Google 推出的 Rendertron 使得 SPA 也能够被不支持执行 Javascript 的搜索引擎爬取渲染后的内容。其原理主要是通过使用 Headless Chrome 在内存中执行 Javascript,并在得到完整内容后,将内容返回给客户端。

    我们通常会将 Rendertron 部署为一个独立的 HTTP 服务,然后为 Web 应用框架配置 Google 官方提供的中间件或者在反向代理上添加相应路由规则,使得能够在检测到搜索引擎爬虫的 UA 时,可以将请求代理给 Rendertron 服务。笔者总结了一下其基本实现原理图,方便大家理解:

    Rendertron 提供了两个主要 API:

    • Render 用于渲染网站内容

    • Screenshot 用于将网站内容截图

    在 SEO 场景下我们使用的是 Render 接口。

    比如当客户端请求我们的网站时,我们服务端可以根据请求头 User Agent 发现是否包含了 Baiduspider/2.0 关键字,如果是, 那么可以认定为当前的客户端是一个百度爬虫此时可以将这个请求代理 Rendertron 服务的 /render/客户端请求地址 路由,让 Rendertron 帮助执行网页内的 Javascript,并将最终内容返回给搜索引擎爬虫。

    使用Rendertron的好处在于我们可以不用考虑服务端渲染的部分,完全按照SPA的模式开发项目,也不用为了兼容服务端渲染而写多余的兼容代码.

    具体实现

    首先我们需要安装Rendertron, 可以在github中找到其安装和使用方法,在安装前最好先安装docker, 目前docker的最新版本以支持傻瓜式安装,所以安装启动都非常方便.

    1.本地运行

    在安装好docker之后, 我们先全局安装rendertron:

    npm install -g rendertron
    

    然后我们需要安装谷歌浏览器(作为合格的开发都应该有谷歌浏览器~),然后就可以用它的cli来启动服务了,我们只需要在命令行执行如下命令:

    rendertron
    

    之后控制台会打印本地服务启动的地址,比如localhost:3000 这个时候我们只需要在地址后面输入我们想渲染的网站即可:localhost:3000:render/你的网站地址, 如下图所示:

    此时我们的rendertron服务已经搭建完成, 接下来我们可以在服务端来实现ssr了,代码如下:

    
    const koa = require('koa');const app = new koa();app.use(async (ctx, next) => {    ctx.type = "html";    if(/Baiduspider\/2\.0/g.ctx.header['user-agent']) {      // 是百度爬虫,则转发到rendertron服务中      ctx.redirect(`http://localhost:3000/render/${ctx.url}`)    }else {        // 渲染正常的路由页面    }
        await next();    })
    app.listen('80');
    

    当然如果我们后端技术栈采用的是express, rendertron有专门的中间件可以使用, 不仅仅可以拦截百度的爬虫,具体用法如下:

    
    const express = require('express');const rendertron = require('rendertron-middleware');
    const app = express();
    app.use(rendertron.makeMiddleware({  proxyUrl: 'http://your-rendertron-instance/render',}));
    // 正常的路由和页面渲染逻辑app.use(...);app.listen(81);
    

    所以为了降低开发成本笔者建议可以采用rendertron的方案, 单独部署一套服务器用来实现ssr. 但是我们需要考虑当网站流量增加时的扩容问题,以及配置搭建反向代理或负载均衡等配套服务。

    后期展望

    后期笔者将会继续带大家探索大前端相关内容, 基本框架如下:

    往期精彩

    当后端一次性丢给你10万条数据, 作为前端工程师的你,要怎么处理?

    基于react/vue搭建一个通用的表单管理配置平台

    javascript进阶必备的二叉树知识

    原生javascript组件开发之Web Component实战

    如何使用css3实现一个类在线直播的队列动画

    最后

    如果想学习更多H5游戏, webpacknodegulpcss3javascriptnodeJScanvas数据可视化等前端知识和实战,欢迎在公号《趣谈前端》加入我们的技术群一起学习讨论,共同探索前端的边界。

    展开全文
  • 服务端渲染理论知识暂且不论,好处是利于首屏加载,以及SEO,具体详情可以去百度一下,因为在公司做了一个react项目,主要用于一个页面的展示功能,正好也使用一下服务端渲染,学习一下,vue用nuxt.js,react用next....
  • 一个基于Vue的virtual dom插件库,按照Vue render 函数的写法,直接将Vue生成的Vnode渲染canvas中。支持常规的滚动操作和一些基础的元素事件绑定。 github 地址: github demo实例:demo 背景 从一个小的需求说起...
  • 编者注:今天呢我们请来了 @有马 同学为我们分享他在做某数据可视化大屏项目的时候使用服务端渲染大屏动画的经验。说到服务端渲染大家一般都想到 Vue 的 SSR 或者 React 的同构吧,不过动画也是可以在服务端渲染的哦...
  • 随着Vue 2.0的发布,服务端渲染一度成为了它的热卖点。在此之前,单页应用的首屏加载时长和SEO的问题,一直困扰着开发者们,也在一定程度上制约着前端框架的使用场景。React提出的服务端渲染方案,较好得解决了上述...
  • 前端面试锦集

    千次阅读 多人点赞 2019-07-20 13:41:45
    WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。 ...
  • 近期总结一一些面试题 都是企业的面试题笔记题 感觉薪资10k下的都会出笔试题 特别高的薪资都是直接技术面试或者是 现场编程 总结很多人的面试题,后期会对于单个知识点再说笔记详细讲解。 ...
  • 使用 Canvas 或者 SVG 渲染 浏览器端图表库大多会选择 SVG 或者 Canvas 进行渲染。对于绘制图表来说,这两种技术往往是可替换的,效果相近。但是在一些场景中,他们的表现和能力又有一定差异。于是,对它们的选择...
  • vue面试题总汇

    千次阅读 多人点赞 2019-02-22 15:26:53
    Sass是基于Ruby的,是在服务端处理的,而Less是需要引入less.js来处理Less代码输出Css到浏览器 axios是什么?怎么使用?描述使用它实现登录功能的流程? 请求后台资源的模块。npm install axios -S装好,然后...
  • 前端每周清单第 11 期:Angular 4.1支持TypeScript 2.3,Vue 2.3优化服务端渲染,优秀React界面框架合集 为InfoQ中文站特供稿件,首发地址为这里;如需转载,请与InfoQ中文站联系。从属于笔者的 Web 前端入门与工程...
  • 服务端渲染

    2021-06-24 16:27:41
    服务端渲染可以使用流行的 headless 环境,例如 puppeteer、headless chrome、node-canvas、jsdom、PhantomJS 等。 这是一些社区贡献的 echarts 服务端渲染方案: https://github.com/hellosean1025/node-echarts ...
  • node+canvas后台绘图功能 后台的canvas的使用不做详细说明。使用方式基本与前端使用一样,我主要说明一下自己在开发过程中遇到的一些坑 安装 npm install canvas 引入canvas到项目 const Canvas = require('canvas'...
  • canvas笔刷可绘制

    2021-07-21 16:43:12
    自定义笔刷,颜色大小图案,应用场景,canvas自己画图然后服务端去3D渲染,等于抽象图变成具像图【!!!zip包只有js部分,vue框架+canvas代码】
  • from: http://phantomjs.org from : http://bluehua.org/tag/phantomjs Applies to: PhantomJS 1.6. This instruction assumes that PhantomJS is ...built and its executable is p
  • 微信小程序实战之秀杰实战教程系列《十一》:对接服务端账目 微信小程序实战之秀杰实战教程系列《十》:服务端实现账目CR 微信小程序实战之秀杰实战教程系列《九》:应用实例教程服务 微信小程序实战之秀杰实战教程...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,908
精华内容 1,963
关键字:

服务端渲染canvas