preact的安装和配置_用preact 还需要安装react react-dom 吗 - CSDN
  • 从0开始搭建preact开发环境 动机 react是个优秀的库,但是如果用到移动端未免有点儿大材小用。于是我在开发个人项目的时候,便...preact在api上做了很多开发上的便捷处理,比如在render方法里传入了propsstate。为...

    从0开始搭建preact开发环境

    动机

    react是个优秀的库,但是如果用到移动端未免有点儿大材小用。于是我在开发个人项目的时候,便选择了preact

    值得一提的是,preact实现了很多的react功能,但是呢,preact也牺牲了react的部分功能。毕竟preact才2KB大小。

    不同

    preact在api上做了很多开发上的便捷处理,比如在render方法里传入了propsstate。为了精简,preact舍弃了部分功能,主要是以下几个功能:

    1. `PropsType`验证(用不怎么到的功能)
    2. `props.children`总是一个数组(差别不怎么大)
    3. `Synthetic Events`(react庞大的原因之一,很大一部分代码用于事件实现)
    4. `render`方法,`preact`接受第三个参数(不怎么用得到)
    复制代码

    妥协处理

    preact-compat提供了完整的reactAPI和功能支持。

    准备工作

    我的文件目录如下

    src
        assets(存放字体、图片等)
        less(存放less文件)
        tsx(存放tsx文件)
        index.html
        index.tsx
    config
        webpack.config.js (开发用配置)
        webpack.build.config.js(生产用配置)
    
    .babelrc (babel配置文件)
    tsconfig.json (typescript 配置文件)
    
    复制代码

    由于是从零开始,因此首先需要确定的就是技术栈。根据以往的经验,我选取的技术栈是 less + typescript。选择使用webpack作为打包工具。那么自然而然地想到以下几个loader:

    1. less-loader(需要less
    2. css-loader
    3. style-loader(用于开发环境)
    4. ts-loader(需要typescript)
    5. url-loader(字体,图片之类的处理)
    6. babel-loader(需要babel-core)

    安装以上几个loader以及对应的需要的包。

    首先安装webpack,我选择使用4.X的版本。 接着安装babel-corewebpack-cli, webpack-dev-server,babel-preset-env

      yarn add webpack webpack-cli webpack-dev-server babel-preset-env --dev
    复制代码

    接着,我们安装代码分离以及开发时需要的插件。注意的是,webpack下,需要安装extract-text-webpack-plugin@next,否则会出错。

        yarn add clean-webpack-plugin html-webpack-plugin extract-text-webpack-plugin@next --dev
    复制代码

    webpack配置

    接下来,我们开始配置webpack的配置文件。我将配置文件分为两类,一个是生产用,一个是开发用。并将它们放到{base_path}/config下面。

      // webpack.config.js
    const html = require('html-webpack-plugin');
    const clean = require('clean-webpack-plugin');
    const path = require('path');
    const extract = require('extract-text-webpack-plugin');
    
    module.exports = {
        entry: './src/index.tsx',
        module: {
            rules: [
                {
                    test: /\.tsx?$/,
                    use: ['babel-loader', 'ts-loader']
                },
                {
                    test: /\.less$/,
                    use: [
                        'style-loader', 
                        {
                            loader: 'css-loader',
                            options: {
                                modules: true
                            }
                        }, 
                        'less-loader'
                    ]
                },
                {
                    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
                    loader: 'url-loader',
                    options: {
                        limit: 1000,
                        name: 'static/fonts/[name].[hash].[ext]'
                    }
                },
                {
                    test: /\.(png|svg|jpg|gif)$/i,
                    loader: 'url-loader',
                    options: {
                        name: 'static/img/[name].[hash].[ext]',
                        limit: 4096
                    }
                }
            ]
        },
        mode: 'development',
        resolve: {
            extensions: ['.jsx', '.js', '.tsx', '.ts']
        },
        plugins: [
            new html({
                template: './src/index.html'
            }),
            new clean([path.resolve('./dist')], {
                root: path.resolve('./')
            })
        ],
        devtool: 'source-map',
        devServer: {
            contentBase: path.resolve("./dist")
        }
    }
    复制代码

    需要注意的是,css modules开启需要css-loadermodules参数设置为true

    生产用配置文件差别不大,主要还是把css文件抽离出来以及进行了代码分离。webpack@4.x简化了代码分离的操作,直接使用splitChunks就行了。

    // webpack.build.config.js
    const html = require('html-webpack-plugin');
    const clean = require('clean-webpack-plugin');
    const path = require('path');
    const extract = require('extract-text-webpack-plugin');
    
    module.exports = {
        entry: path.resolve('./src/index.tsx'),
        module: {
            rules: [
                {
                    test: /\.tsx?$/,
                    use: ['babel-loader', 'ts-loader']
                },
                {
                    test: /\.less$/,
                    use: extract.extract({
                        use: [
                            {
                                loader: 'css-loader',
                                options: {
                                    sourceMap: true
                                }
                            },
                            {
                                loader: 'less-loader',
                                options: {
                                    sourceMap: true
                                }
                            }
                        ]
                    })
                },
                {
                    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
                    loader: 'url-loader',
                    options: {
                        limit: 1000,
                        name: 'static/fonts/[name].[hash].[ext]'
                    }
                },
                {
                    test: /\.(png|svg|jpg|gif)$/i,
                    loader: 'url-loader',
                    options: {
                        name: 'static/img/[name].[hash].[ext]',
                        limit: 4096
                    }
                }
            ]
        },
        mode: 'production',
        resolve: {
            extensions: ['.jsx', '.js', '.tsx', '.ts']
        },
        optimization: {
            splitChunks: {
                cacheGroups: {
                    vender: {
                        name: 'vendor',
                        minSize: 0,
                        chunks: 'initial',
                        test: /node_modules/,
                    }
                }
            },
            runtimeChunk: {
                name: 'manifest'
            },
            minimize: true
        },
        output: {
            path: path.resolve('./dist'),
            filename: 'static/js/[name].[hash:8].js',
            chunkFilename: 'static/js/[name].[chunkhash:8].js',
            publicPath: './'
        },
        plugins: [
            new html({
                template: './src/index.html'
            }),
            new clean([path.resolve('./dist')], {
                root: path.resolve('./')
            }),
            new extract({
                filename: 'static/css/[name].[hash:8].css',
                allChunks: true
            })
        ]
    }
    
    复制代码

    值得一提的是,我在处理路劲的时候都用到了path.resolve。此时,./目录对应的并非config文件所在目录,而是项目根目录。

    接着我们增加两条命令到package.json里面:

      "scripts": {
        "serve": "webpack-dev-server --config ./config/webpack.config.js",
        "build": "webpack --config ./config/webpack.build.config.js"
      }
    复制代码

    完了吗?当然没有,还有最主要的一点,就是babel的配置问题。

    babel配置

    babel除了配置perset以外,还需要配置一个很重要的transform-react-jsx插件。这个插件目的是用来转换jsx语法的。然而,它默认是将jsx语法转换为React.createElement这个函数包装的virtual dom。我们需要更改一下,按照传统,我们使用preact.h替代。

    // .babelrc
    {
        "presets": [
            [
                "env",
                {
                    "modules": false
                }
            ]
        ],
        "plugins": [
            [
                "transform-react-jsx",
                {
                    "pragma": "preact.h"
                }
            ]
        ]
    }
    复制代码

    typescript配置

    这里只有一个地方需要注意的,那就是jsx语法应该保留,不能转换。根据以上的配置文件,ts文件首先传递给ts-loader,接着传递给babel-loader。我们把jsx语法交给babel转换,因此需要保留。

    {
        "compilerOptions": {
            "target": "es2016",
            "moduleResolution": "node",
            "jsx": "preserve",
            "sourceMap": true
        }
    }
    复制代码

    结语

    webpack的配置真的是一言难尽啊。。。另外开发过程中,也有需要注意的地方。css modules不要用import,应该用require。当然,你得自己定义一个require。另外,jsx语法的文件,都必须导入preact的包,并且还有讲究。

    import * as preact from 'preact'; // 必须导入成 * as preact
    
    declare function require(...args: any[]): any; // 定义require函数
    
    const test = require('../less/test.less'); 
    
    export default class App extends preact.Component<any, any> {
        constructor(props) {
            super(props);
        }
        
        render(props, state) {
            return (
                <div className={ test.test }>hello world!</div>
            )
        }
    }
    
    复制代码

    基本上就是如此了,webpack的配置还是很麻烦。并没有传说中的0配置那么神。需要自定义功能的时候,还是需要熟练掌握webpack才能游刃有余。

    配置弄完又是一个下午没有了。。。

    展开全文
  • In this tutorial, we are going to explore how to build a server-side rendered app with Preact. preact-router will be used for routing, unistore for state management and Webpack for JS bundling. Some e...

    In this tutorial, we are going to explore how to build a server-side rendered app with Preact. preact-router will be used for routing, unistore for state management and Webpack for JS bundling. Some existing knowledge of Preact, Unistore, and Webpack might be needed.

    在本教程中,我们将探索如何使用Preact构建服务器端渲染的应用程序。 preact-router将用于路由, unistore用于状态管理, Webpack用于JS捆绑。 可能需要一些有关Preact,Unistore和Webpack的现有知识。

    为什么要使用服务器端渲染的应用程序? ( Why Server-Side Rendered Apps? )

    We can all agree that Single Page Apps are a very popular way of building modern web applications. When it comes to SPAs, there are two ways in which you can render the content of the app to your users; Client Side Rendering and Server-Side Rendering.

    我们都可以同意单页应用程序是构建现代Web应用程序的一种非常流行的方法。 对于SPA,可以通过两种方式向用户呈现应用程序的内容。 客户端渲染和服务器端渲染。

    With Client Side Rendering, whenever a user opens up the app, it sends a request to load up the layout, HTML, CSS, and JavaScript. This is all fine. But in cases (all the time) which the content of the application is dependent on the completion of successfully loading the JS scripts, this can be a problem. This means users would be forced to view an empty or a preloader while waiting for the scripts to finish loading.

    使用客户端渲染,每当用户打开应用程序时,它都会发送请求以加载布局,HTML,CSS和JavaScript。 一切都很好。 但是,在某些情况下(始终),应用程序的内容取决于成功加载JS脚本的完成情况,这可能是一个问题。 这意味着在等待脚本完成加载时,用户将被迫查看空白或预加载器。

    Server-Side Rendering doesn't operate like that, with SSR, your initial request straight up loads the page, layout, CSS, JavaScript, and content. SSR makes sure that data is properly initialized at render time.

    服务器端渲染不能像SSR这样操作,您的初始请求会直接加载页面,布局,CSS,JavaScript和内容。 SSR确保在渲染时正确初始化数据。

    Another advantage Server-Side Rendering has over Client Side Rendering is SEO.

    服务器端渲染相对于客户端渲染的另一个优势是SEO。

    Technologies Let's go over the technologies we'll be using to build the Server-Side Rendered App;

    技术让我们研究一下将用于构建服务器端渲染应用程序的技术。

    Preact Preact is a 3kb alternative to React with the same API. It aims to offer a development experience similar to React, albeit with some features stripped away such as PropTypes and Children. Unistore Unistore is a 650b centralized state container with component bindings for React and Preact. It's small size means it complements Preact nicely. Preact Router Preact Router helps to manage route in Preact applications. provides a component that conditionally renders its children when the URL matches their path. Webpack Webpack is bundler that helps to bundle JavaScript files for usage in a browser. Enough said. preact-render-to-string

    Preact Preact是具有相同API的React的3kb替代方案。 它旨在提供类似于React的开发体验,尽管其中剥离了一些功能(例如PropTypes和Children)Unistore Unistore是一个650b集中式状态容器,具有用于React和Preact的组件绑定。 它体积小,意味着可以很好地补充Preact。 Preact路由器 Preact路由器可帮助管理Preact应用程序中的路由。 提供一个组件,当URL匹配其子路径时,该子组件有条件地呈现其子项。 Webpack Webpack是捆绑程序,可帮助捆绑JavaScript文件以在浏览器中使用。 说够了。 预先渲染到字符串

    使用Preact构建SSR应用 ( Build a SSR app with Preact )

    Building this app would be divided into two. We'll first build the server-side of the code which will be in Node and Express and then we'll code the Preact part of the code.

    构建此应用程序将分为两部分。 我们将首先在Node和Express中构建代码的服务器端,然后对代码的Preact部分进行编码。

    The idea is to create a Preact app as it were and hook it up to a Node server using the preact-render-to-string package. It allows for rendering JSX and Preact components to an HTML string which can then be used in a server. This means we'll be creating Preact components in a src folder and hook it up to the Node server file.

    这个想法是照原样创建一个Preact应用程序,并使用preact-render-to-string程序包preact-render-to-string其连接到Node服务器。 它允许将JSX和Preact组件呈现为HTML字符串,然后可以在服务器中使用。 这意味着我们将在src文件夹中创建Preact组件,并将其连接到Node服务器文件。

    The first thing to do would be to create the directory for the project and create the different folders that would be needed. Create a folder named preact-unistore-ssr and run the command npm init --y inside the folder. That creates a minimal package.json and an accompanying package-lock.json.

    要做的第一件事是为项目创建目录并创建所需的其他文件夹。 创建一个名为preact-unistore-ssr的文件夹, npm init --y在该文件夹中运行命令npm init --y 。 这将创建一个最小的package.json和一个随附的package-lock.json

    Next, let's install some of the tools we'll be needing for this project. Open up the package.json file and edit with the code below, then run the npm i command.

    接下来,让我们安装该项目所需的一些工具。 打开package.json文件并使用以下代码进行编辑,然后运行npm i命令。

    {
      "name": "preact-unistore-ssr",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "babel-cli": "^6.26.0",
        "babel-core": "^6.26.0",
        "babel-loader": "^7.1.2",
        "babel-plugin-transform-react-jsx": "^6.24.1",
        "babel-preset-env": "^1.6.1",
        "file-loader": "^1.1.11",
        "url-loader": "^1.0.1",
        "webpack": "^3.11.0",
        "webpack-cli": "^2.0.13"
      },
      "dependencies": {
        "express": "^4.16.2",
        "preact": "^8.2.6",
        "preact-render-to-string": "^3.7.0",
        "preact-router": "^2.6.0",
        "unistore": "^3.0.4"
      }
    }

    That will install all the packages needed for this application. In the devDependencies object, we have some babel packages that will help with transpiling ES6 code. file-loader and url-loader are Webpack plugins that help with importing files, assets, modules e.t.c.

    这将安装此应用程序所需的所有软件包。 在devDependencies对象中,我们有一些babel软件包,它们将有助于转译ES6代码。 file-loaderurl-loader是Webpack插件,可帮助导入文件,资产,模块等

    In the dependencies object, we install packages like Express, Preact, preact-render-to-string, preact-router, and unistore.

    dependencies对象中,我们安装了Express,Preact,preact-render-to-string,preact-router和unistore之类的包。

    Next, let's create a Webpack config file, create a file named webpack.config.js in the root of the project and edit it with the code below:

    接下来,让我们创建一个Webpack配置文件,在项目的根目录中创建一个名为webpack.config.js文件,并使用以下代码对其进行编辑:

    const path = require("path");
    
    module.exports = {
        entry: "./src/index.js",
        output: {
            path: path.join(__dirname, "dist"),
            filename: "app.js"
        },
        module: {
            rules: [
                {
                    test: /\.js$/,
                    loader: "babel-loader",
                }
            ]
        }
    };
    

    In the webpack config above, we define the entry point to be a src/index.js file and the output to be dist/app.js and also set the rules for using babel. The entry point file does not exist yet but that will be created later.

    在上面的webpack配置中,我们将入口点定义为src/index.js文件,将输出点定义为dist/app.js ,并设置使用babel的规则。 入口点文件尚不存在,但稍后会创建。

    Since we're using Babel, we'll need to create a .babelrc file in the root of the project and put in our config.

    由于使用的是Babel,因此需要在项目的根目录中创建一个.babelrc文件,并将其放入配置中。

    //.babelrc
    {
        "plugins": [
            ["transform-react-jsx", { "pragma": "h" }]
        ],
        "presets": [
            ["env", {
                "targets": {
                    "node": "current",
                    "browsers": ["last 2 versions"]
                }
            }]
        ]
    }
    

    生成Preact应用 ( Build the Preact App )

    Next up, we'll begin to create files for the Preact side of things. Create a src folder and create the following files in it.

    接下来,我们将开始为事物的Preact端创建文件。 创建一个src文件夹并在其中创建以下文件。

    store/store.js
    About.js
    App.js
    index.js
    router.js

    Let's begin to edit the files with the necessary code. We'll start with the store.js file. This will contain the store data and actions.

    让我们开始使用必要的代码编辑文件。 我们将从store.js文件开始。 这将包含商店数据和操作。

    import createStore from 'unistore'
    
    export let actions = store => ({
        increment(state) {
            return { count: state.count + 1 }
        },
        decrement(state) {
            return { count: state.count - 1 }
        }
    })
    
    export default initialState => createStore(initialState)

    in the code block above, we are exporting some set of actions, which basically increments and decrements the value of the count by 1. The actions will always receive state as first parameter and any other params may come next. The createStore function which is used to initialize the store in Unistore is also exported.

    在上面的代码块中,我们正在导出一些操作,这些操作基本上将count的值增加或减少1。这些操作将始终接收状态作为第一个参数,接下来可能会出现其他任何参数。 用于初始化Unistore中的商店的createStore函数也会被导出。

    Next up, we'll edit the router.js file. This contains the setup for the routes we'll be using in the app.

    接下来,我们将编辑router.js文件。 这包含我们将在应用程序中使用的路线的设置。

    import { h } from 'preact'
    import Router from 'preact-router'
    
    import { App } from "./App";
    import { About } from "./About";
    
    export default () => (
        <Router>
            <App path="/" />
            <About path="/about" />
        </Router>
    )

    preact-router makes it so easy to define routes. all you have to do is import the routes and make them the children of the Router component. You can then set a prop of path to each component so that preact-router knows which component to serve for a route.

    preact-router使定义路由变得如此容易。 您所要做的就是导入路由,并将其设置为Router组件的子级。 然后,您可以为每个组件设置pathprop ,以使preact-router知道要为路由使用哪个组件。

    There are mainly just two routes in the application, the App.js component serves as the home route and the About.js component serves as the about page.

    应用程序中主要只有两条路由,其中App.js组件充当主路由,而About.js组件充当About.js页面。

    Next is the About.js file and you can proceed to edit with the following:

    接下来是About.js文件,您可以进行以下编辑:

    import { h } from "preact";
    import { Link } from "preact-router/match";
    
    export const About = () => (
        <div>
            <p>This is a Preact app being rendered on the server. It uses Unistore for state management and preact-router for routing.</p>
            <Link href="/">Home</Link>
        </div>
    );

    It's a simple component that has a short description and a Link component that leads to the home route.

    这是一个简单的组件,具有简短说明,而Link组件则指向本地路由。

    Let's open up App.js which serves as the home route and edit with the necessary code.

    让我们打开用作本地路由的App.js并使用必要的代码进行编辑。

    import { h } from 'preact'
    import { Link } from 'preact-router'
    import { connect } from 'unistore/preact'
    
    import { actions } from './store/store'
    
    export const App = connect('count', actions)(
        ({ count, increment, decrement }) => (
          <div class="count">
            <p>{count}</p>
            <button class="increment-btn" onClick={increment}>Increment</button>
            <button class="decrement-btn" onClick={decrement}>Decrement</button>
            <Link href="/about">About</Link>
          </div>
        )
      )

    So what's happening here? The connect function is imported, as well as the actions function. In the App component, the state value, count is exposed as well as the increment and decrement actions. The increment and decrement actions are both hooked up to different buttons with the onClick event handler.

    那么这里发生了什么? connect功能以及actions功能均已导入。 在App组件中,将显示状态值, count以及incrementdecrement操作。 incrementdecrement操作都通过onClick事件处理程序连接到不同的按钮。

    The index.js file is the entry point for Webpack if you remember too well, it's going to serve as the parent component for all other components in the Preact app. Open up the file and edit with the code below.

    如果您记不清楚的话, index.js文件是Webpack的入口点,它将用作Preact应用程序中所有其他组件的父组件。 打开文件并使用以下代码进行编辑。

    // index.js
    import { h, render } from 'preact'
    import { Provider } from 'unistore/preact'
    import Router from './router'
    
    import createStore from './store/store'
    
    const store = createStore(window.__STATE__)
    
    const app = document.getElementById('app')
    
    render(
        <Provider store={store}>
            <Router />
        </Provider>,
        app,
        app.lastChild
    )

    In the code block above, the Provider component is imported. It's important to specify the working environment if it's preact or react. We also import the Router component from the router.js file and the createStore function is equally imported from the store.js file.

    在上面的代码块中,导入了Provider组件。 重要的是要确定工作环境是预先制定还是做出React。 我们还从router.js文件导入Router组件,并且同样从store.js文件导入createStore函数。

    The const store = createStore(window.__STATE__) line is used to pass initial state from the server to client since we're building a SSR app afterall.

    const store = createStore(window.__STATE__)行用于将初始状态从服务器传递到客户端,因为我们毕竟是在构建SSR应用程序。

    Finally, in the render function, we wrap the Router component inside the Provider component thereby making the store available to all child components.

    最后,在render函数中,我们将Router组件包装在Provider组件内,从而使存储可用于所有子组件。

    That's a wrap on the client side of things. We'll now move to the server-side of the app.

    那是客户端的事情。 现在,我们将移至应用程序的服务器端。

    构建节点服务器 ( Build the Node Server )

    To start with, a server.js file will be created, this will house the Node app that will be used for the server-side rendering.

    首先,将创建一个server.js文件,该文件将包含将用于服务器端渲染的Node应用程序。

    // server.js
    const express = require("express");
    const { h } = require("preact");
    const render = require("preact-render-to-string");
    import { Provider } from 'unistore/preact'
    const { App } = require("./src/App");
    const path = require("path");
    
    import Router from './src/router'
    import createStore from './src/store/store'
    
    const app = express();
    
    const HTMLShell = (html, state) => `
        <!DOCTYPE html>
        <html>
            <head>
                <meta charset="utf-8">
                <meta name="viewport" content="width=device-width, initial-scale=1">
                <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
                <title> SSR Preact App </title>
            </head>
            <body>
                <div id="app">${html}</div>
                <script>window.__STATE__=${JSON.stringify(state).replace(/<|>/g, '')}</script>
                <script src="./app.js"></script>
            </body>
        </html>`
    
    app.use(express.static(path.join(__dirname, "dist")));
    
    app.get('**', (req, res) => {
        const store = createStore({ count: 0, todo: [] })
    
        let state = store.getState()
    
        let html = render(
            <Provider store={store}>
                <Router />
            </Provider>
        )
    
        res.send(HTMLShell(html, state))
    })
    
    app.listen(4000);

    Let's break this down into bits.

    让我们将其分解为几部分。

    const express = require("express");
    const { h } = require("preact");
    const render = require("preact-render-to-string");
    import { Provider } from 'unistore/preact'
    const { App } = require("./src/App");
    const path = require("path");
    
    import Router from './src/router'
    import createStore from './src/store/store'
    
    const app = express();

    In the code block above, we import the packages needed for the Node server, such as express, path, we also import preact, the Provider component from unistore and most importantly the preact-render-to-string package which enables us to do server-side rendering. The routes and store is also imported from their respective files.

    在上面的代码块,我们导入所需的节点服务器的软件包,如expresspath ,我们也进口preact中, Provider从零部件unistore ,最重要的是preact-render-to-string包,它使我们能够做服务器侧渲染。 路线和存储也从它们各自的文件中导入。

    const HTMLShell = (html, state) => `
        <!DOCTYPE html>
        <html>
            <head>
                <meta charset="utf-8">
                <meta name="viewport" content="width=device-width, initial-scale=1">
                <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
                <title> SSR Preact App </title>
            </head>
            <body>
                <div id="app">${html}</div>
                <script>window.__STATE__=${JSON.stringify(state).replace(/<|>/g, '')}</script>
                <script src="./app.js"></script>
            </body>
        </html>`

    In the code block above, we create the base HTML that will be used for the app. In the HTML code, the state is initialiazed in the script section. The HTMLShell function accepts two params, the first one is html, this will be the output gotten from the preact-render-to-string, html is then injected inside the HTML code. The second param is the state.

    在上面的代码块中,我们创建将用于该应用程序的基本HTML。 在HTML代码中,状态在script部分中初始化。 HTMLShell函数接受两个参数,第一个是html ,这是从preact-render-to-string获得的输出,然后将html注入HTML代码中。 第二个参数是状态。

    app.use(express.static(path.join(__dirname, "dist")));
    
    app.get('**', (req, res) => {
        const store = createStore({ count: 0})
    
        let state = store.getState()
    
        let html = render(
            <Provider store={store}>
                <Router />
            </Provider>
        )
    
        res.send(HTMLShell(html, state))
    })
    
    app.listen(4000);

    In the first LOC, we tell Express to use the dist when serving static files, if you remember too well, the app.js is inside the dist folder. Next, we set a route for any request that comes into the app with app.get(**) and this is where the work lies. This first thing to do is to initialize the store and it's state and then create a variable that holds the value of the state.

    在第一个LOC中,我们告诉Express在提供静态文件时使用dist ,如果您还记得的太久的话, app.js位于dist文件夹中。 接下来,我们使用app.get(**)为进入应用程序的任何请求设置路由,这就是工作所在。 首先要做的是初始化存储及其状态,然后创建一个保存状态值的变量。

    Then, preact-render-to-string which was imported as render is used to render the client side Preact app alongside the Router which holds the route and Provider which provides the store to every child component.

    然后,使用作为render器导入的preact preact-render-to-string来渲染客户端Preact应用程序,该应用程序与保存路由的Router以及为每个子组件提供存储的Provider一起呈现。

    With that done, we can finally run the app and see what it looks like, before you do that, add the code block below to the package.json file.

    完成之后,我们最终可以运行该应用程序并查看其外观,然后再将下面的代码块添加到package.json文件中。

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start:client": "webpack -w",
        "start:server": "babel-node server.js",
        "dev": "npm run start:client & npm run start:server"
      },

    These are scripts that allows us to get the app up and running, run the command npm run dev in your terminal and go to http://localhost:4000. The app should be up and running and you should get a display similar to the one below.

    这些脚本使我们能够启动并运行该应用程序,在您的终端中运行命令npm run dev并转到http:// localhost:4000 。 该应用程序应已启动并正在运行,并且您将得到与以下显示类似的显示。

    添加CSS样式 ( Adding CSS Styling )

    Now that are views are done and the client is hooked up to the server, let's add some styling to the app. We'll need to let Webpack know that it needs to bundle CSS files. To do that, style-loader and css-loader needs to be added to the app and both can be installed by running the command below.

    现在已经完成视图,并且客户端已连接到服务器,让我们为应用程序添加一些样式。 我们需要让Webpack知道它需要捆绑CSS文件。 为此,需要将style-loadercss-loader添加到应用程序中,并且都可以通过运行以下命令来安装两者。

    npm i css-loader style-loader --save-dev

    Once the installation is done, head over to the webpack.config.js file and add the the code below inside the rules array.

    安装完成后,转到webpack.config.js文件,然后将以下代码添加到rules数组内。

    {
        test: /\.css$/,
        use: [ 'style-loader', 'css-loader' ]
    }

    We can now create a index.css file inside the src folder and edit with the following code:

    现在,我们可以在src文件夹中创建一个index.css文件,并使用以下代码进行编辑:

    body {
      background-image: linear-gradient(to right top, #2b0537, #820643, #c4442b, #d69600, #a8eb12);
      height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      text-align: center;
    }
    a {
      display: block;
      color: white;
      text-decoration: underline;
    }
    p {
      color: white
    }
    .count p {
      color: white;
      font-size: 60px;
    }
    button:focus {
      outline: none;
    }
    .increment-btn {
      background-color: #1A2C5D;
      border: none;
      color: white;
      border-radius: 3px;
      padding: 10px 20px;
      font-size: 14px;
      margin: 0 10px;
    }
    .decrement-btn {
      background-color: #BC1B1B;
      border: none;
      color: white;
      border-radius: 3px;
      padding: 10px 20px;
      font-size: 14px;
      margin: 0 10px;
    }

    In the index.js file, add this code at the top of the file too. import './index.css';

    index.js文件中,也将此代码添加到文件顶部。 import './index.css';

    Now the page should display nicely like this.

    现在,页面应该显示得很好。

    结论 ( Conclusion )

    In this tutorial, we've seen how to create a Server-Side Rendered Preact app and explored the advantages of building server-side rendered apps. We also saw how to use Unistore for basic state management and how to hook up state from the server to the frontend using window.__STATE__.

    在本教程中,我们了解了如何创建服务器端渲染的Preact应用程序,并探讨了构建服务器端渲染的应用程序的优势。 我们还看到了如何使用Unistore进行基本状态管理,以及如何使用window.__STATE__将状态从服务器连接到前端。

    You should now have an idea on how to render a Preact app on the server. The basic gist is to initially render the app on the server FIRST and then render the components on the browser.

    现在,您应该对如何在服务器上呈现Preact应用程序有所了解。 基本要点是首先在服务器上首先渲染应用程序,然后在浏览器上渲染组件。

    The code for this tutorial can be viewed on Github for your perusal.

    您可以在Github上查看本教程的代码以供仔细阅读。

    翻译自: https://scotch.io/tutorials/build-a-ssr-app-with-preact-unistore-and-preact-router

    展开全文
  • 此分支已集成react与preact。 背景 最近接手了互动视频的项目,做了一个月的运营活动。跟基础功能不同,运营活动更为轻量。因此许多同事并不想用那么“重”的React。但同时,大家由于之前度过React的上手痛苦期后,...

    原文链接

    本文使用starter-kit:steamer-react react分支。此分支已集成react与preact。

    背景

    最近接手了互动视频的项目,做了一个月的运营活动。跟基础功能不同,运营活动更为轻量。因此许多同事并不想用那么“重”的React。但同时,大家由于之前度过React的上手痛苦期后,开始体会到React的许多好处,裸写运营活动的时候,又开始对React的好处念念不忘记:良好的组件化、解放js能力的jsx等。
    因此,寻找轻量化的类React解决方案便提上日程。

    Preact的优点

    选型的时候,首先有几个考量:

    • 开源社区有较多star(认可)
    • 较好的性能和兼容性
    • api跟React接近
    • 足够的框架周边,配置redux,router等使用
    • 团队成员有能力维护的

    基本上以上几点,Preact都能够很好的满足,因此最终选定为团队的类React轻量化框架进行使用和研究。

    开源社区有较多star(认可)

    相比起react-liteDeku, Virtual-DOM,Preact虽然不是最多的star,但也能排第2,也具备测试用例,且作者开通了gitter chat跟开发者保持联系,某天在上面留言,作者也是回复得很迅速。

    较好的性能和兼容性

    性能方面,Preact也不俗。加载性能方面,由于本身的bundle在gzip后大概只有3kb,跟React相比小太多,自由就有优势。渲染性能方面,参考了一篇JS WEB FRAMEWORKS BENCHMARK系列测评文章,发现Preact在创建、更新、删除节点等方面,都有良好的表现。

    第一次性能测试:

    第二次性能测试:


    包大小:

    framework version minimized size
    React 0.14.3 136.0kb
    React-lite 0.15.6 25kb
    Preact 5.6.0 10kb
    Deku 2.0.0-rc16 51.2kb
    Virtual Dom 2.1.1 50.5kb

    除了性能的良好表现,此框架的浏览器兼容性也不错,能兼容目前的主流浏览器,并且在添加polyfill的情况下,能够兼容在国内还有不少份额的IE8,确实是不少还需要兼容IE8开发者的福音。

    api跟React接近

    Preact的常用api基本跟React一致,这使得对React熟悉的开发者,完全没有上手的难度,Preact作者单独开辟了一个文档Differences to React,介绍React与Preact的异同。Preact主要缺少的React Api有PropType,Children, 和 Synthetic Events(合成事件)。作者解释道,PropType其实许多人都不使用,并不影响开发; Children其实是数组,所以也并不是必须的;而合成事件,由于不需要过度考虑不同浏览器对事件处理的异同,所以也并没有做过度封装。如果真的想使用以上这些缺失的React Api,作者也提供了preact-compat,使用的时候,在Webpack上的external这样替换便可:

    {
        // ...
        resolve: {
            alias: {
                'react': 'preact-compat',
                'react-dom': 'preact-compat'
            }
        }
        // ...
    }复制代码

    足够的框架周边,配置redux,router等使用

    对于React开发者来说,最常用的就是redux, router这些周边的插件。而Preact也有提供preact-reduxpreact-router,甚至还有帮助Preact做同构直出的preact-render-to-string

    团队成员有能力维护的

    Preact项目的框架小而美,合并成的dist文件也只有500行左右,比较容易学习和维护。若团队选择此框架作为React的轻量解决方案的话,我们最好能具备维护和开发此框架的能力,这能够在遇到bug的时候第一时间修复,而且能够很好地开发一些组件,提升框架的开发效率。

    如何上手及如何和React在同一构建下使用

    作者在Getting Started里有比较好的介绍。其实不外乎就2点差异:

    • 引入preact与引入react的差异。
      引入preact的时候,大概是这样的:

      import preact, { h, render, Component } from 'preact';复制代码

      而引入react的时候,大概是这样的:

      import React, { Component, PropTypes } from 'react';
      import { render } from 'react-dom';复制代码
    • 编译所需的插件差异。
      preact的jsx编译,主要借助babel-plugin-transform-react-jsx,而react则是借助babel-preset-react

    如果你想在一个构建里面同时使用React和Preact(有的页面使用React,有的用Preact),你可以通过Webapck的loader include或者exclude,然后凭路径区分。而我在steamer-react的react-preact分支里的处理是直接用文件名后缀。如果是有React相关引入的,则用.js后缀,而有Preact相关引入的,则用.jsx后缀。

    Preact的实现简介

    粗略看了一下Preact的实现,简单介绍一下。

    Virtual Dom

    Virtual Dom算是类React框架的最大卖点。Preac作者写了一篇WTF is JSX。主要就是借助babel-plugin-transform-react-jsx的能力,里面有个pragma参数,用于设定用什么函数来做virtual dom的转换。此处定义的是preact.h

     ["transform-react-jsx", { "pragma":"preact.h" }]复制代码

    所以,你会看到编译后,有类似的代码:

    _preact2.default.h(
        'p',
        { className: 'info-content' },
        item.des
    )复制代码

    查看源码,preac定义了h的函数,用于将传入的值转换成virtual dom:

    function h(nodeName, attributes, firstChild) {
        // some code here
    }复制代码

    所以,如果传入上面的p和对应属性,则会转换成下面的对象:

    VNode {nodeName: "p", attributes: {class:"info-content"}, children: undefined, key: undefined}复制代码

    但virtual dom需要转换成真实的dom,还需要一个函数进行转换。在Preact中,大体是通过这个流程,然后最终转换成真实dom:

    render (类似于react-dom里的render,主入口,触发渲染) => diff => idiff (看起来应该是做dom diff) => createNode (生成真实dom)复制代码

    组件化与生命周期

    组件化也是类React框架的一大特色。Preact的组件化,主要是通过Component这一方法来实现的。主要包括,setState,render以及一众生命周期。主要的渲染,生命周期的触发,也主要定义在renderComponentsetComponentProps方法内。用户的自定义组件只需要继承Component就可以自由使用Preact组件化的能力。

    事件机制

    Preact并没有像React那样自己实现了一套事件机制,主要还是用浏览器自带的能力。因此,在给生成真实dom并通过setAccessor给dom插入属性的时候,有这么一段代码:

    else if ('o' === name[0] && 'n' === name[1]) {
         var l = node._listeners || (node._listeners = {});
         name = toLowerCase(name.substring(2));
         if (value) {
             if (!l[name]) node.addEventListener(name, eventProxy);
         } else if (l[name]) node.removeEventListener(name, eventProxy);
         l[name] = value;
     }复制代码

    判断属性中是否含有on,也就是在看,有没有on开头的属性(一般就是事件)。然后就进行addEventListener或者removeEventListener。看起来跟我们写原生js的事件绑定没有什么区别。

    如有错误,恳请斧正。

    展开全文
  • 就像React许多其他类似的库一样, Preact是虚拟DOM组件范例的实现。 与React不同,它只有3KB的大小,并且在速度方面也胜过它。 它是由杰森·米勒(Jason Miller)创建的,并以著名的宽松开源MIT许可证提供。 ...

    就像React和许多其他类似的库一样, Preact是虚拟DOM组件范例的实现。 与React不同,它只有3KB的大小,并且在速度方面也胜过它。 它是由杰森·米勒(Jason Miller)创建的,并以著名的宽松和开源MIT许可证提供。

    为什么要使用Preact?

    徽标徽标 Preact是React的轻量级版本。 如果您喜欢使用React构建视图,但是性能,速度和大小是您的首要任务,例如移动Web应用程序或渐进式Web应用程序,则您可能更喜欢使用Preact作为轻量级替代方案。

    无论您是开始一个新项目还是开发一个现有项目,Preact都可以为您节省大量时间。 你并不需要推倒重来努力学习新的图书馆,因为它类似,和兼容,作出React-到如此地步,可以使用现有的React封装,它只有一些混淆,多亏了兼容层preact-compat

    利弊

    React和Preact之间有很多区别,我们可以概括为三点:

    • 功能和API :Preact仅包括React API的一个子集,而不是React中所有可用的功能。
    • 大小 :Preact比React小得多。
    • 性能 :Preact比React快。

    那里的每个图书馆都有自己的优缺点,只有优先考虑才能帮助您确定哪个图书馆适合您的下一个项目。 在本节中,我将尝试列出这两个库的优缺点。

    事前优点

    • Preact比React轻巧,小巧(压缩后只有3KB)并且比React快(请参阅这些测试 )。 您也可以通过此链接在浏览器中运行性能测试。
    • Preact在很大程度上与React兼容,并且具有与React相同的ES6 API,这使得采用Preact作为用于在项目中构建用户界面的新库或出于性能原因将React与Preact交换而变得非常容易。
    • 它具有良好的文档和可从官方网站上获得的示例。
    • 它具有功能强大且正式的CLI,可快速创建新的Preact项目,而无需担心Webpack和Babel的配置。
    • 许多功能都受React上已经完成的所有工作的启发。
    • 它还具有独立于React的自己的高级功能集,例如Linked State

    React专家

    • React支持单向数据绑定。
    • 它得到了一家大型公司Facebook的支持。
    • 官方网站和网络上的优质文档,示例和教程。
    • 大型社区。
    • 在Facebook网站上使用,该网站在全球拥有数百万的访问者。
    • 具有针对Chrome的官方开发人员调试工具扩展程序。
    • 它具有Create React App项目样板,可快速创建零配置的项目。
    • 它具有结构良好且复杂的代码库。

    React缺点

    • 与Preact或其他现有的类似库相比,React具有相对较大的大小。 (React后的缩小的源文件大小约为136KB,缩小并压缩后约为42KB。)
    • 它比Preact慢。
    • 由于其复杂的代码库,新手开发人员很难做出贡献。

    注意:我在撰写本文时列出的另一个缺点是,React具有与BSD许可证配对的授权专利条款,使其在法律上不适合某些用例。 但是, 在2017年9月 ,React许可证转换为MIT,从而解决了这些许可证问题。

    预设缺点

    • Preact仅支持无状态功能组件和基于ES6类的组件定义,因此没有createClass
    • 不支持上下文
    • 不支持React propTypes。
    • 比React更小的社区。

    Preact CLI入门

    Preact CLI是Preact的作者Jason Miller创建的命令行工具。 它使创建新的Preact项目非常容易,而又不会陷入配置复杂性的困扰,所以让我们从安装它开始。

    打开终端(Linux或macOS)或命令提示符(Windows),然后运行以下命令:

    npm i -g preact-cli@latest

    假设您在本地开发计算机上安装Node和NPM ,它将安装Preact CLI的最新版本。

    现在,您可以使用以下命令创建项目:

    preact create my-app

    或与此相关的东西,如果您想以交互方式创建您的应用程序:

    preact init

    接下来,在应用程序的根文件夹中导航并运行以下命令:

    npm start

    这将启动实时重载开发服务器。

    最后,当您完成应用程序的开发时,可以使用以下代码构建生产版本:

    npm run build

    为您的第一个Preact应用揭秘

    成功安装Preact CLI并生成应用后,让我们尝试了解使用Preact CLI生成的简单应用。

    Preact CLI生成以下目录结构

    ├── node_modules
    ├── package.json
    ├── package-lock.json
    └── src
        ├── assets
        ├── components
        │   ├── app.js
        │   └── header
        ├── index.js
        ├── lib
        ├── manifest.json
        ├── routes
        │   ├── home
        │   └── profile
        └── style
            └── index.css

    components文件夹包含Preact组件,而routes文件夹包含用于每个应用程序路由的页面组件。 您可以将lib文件夹用于任何外部库,将CSS样式的style文件夹,以及用于图标和其他图形的assets使用。

    请注意manifest.json文件,该文件类似于package.json但适用于PWA(渐进式Web应用程序)。 借助Preact CLI,您可以立即获得完美得分的PWA。

    现在,如果打开项目的package.json文件,您将看到主入口点设置为src/index.js 这是此文件的内容:

    import './style';
    import App from './components/app';
    
    export default App;

    免费学习PHP!

    全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

    原价$ 11.95 您的完全免费

    如您所见, index.js./components/app**导入样式和App组件,然后将其默认导出。

    现在,让我们看看./components/app里面的./components/app

    import { h, Component } from 'preact';
    import { Router } from 'preact-router';
    
    import Header from './header';
    import Home from '../routes/home';
    import Profile from '../routes/profile';
    
    export default class App extends Component {
        handleRoute = e => {
            this.currentUrl = e.url;
        };
    
        render() {
            return (
                <div id="app">
                    <Header />
                    <Router onChange={this.handleRoute}>
                        <Home path="/" />
                        <Profile path="/profile/" user="me" />
                        <Profile path="/profile/:user" />
                    </Router>
                </div>
            );
        }
    }

    该文件导出默认类App ,该类扩展了从preact包中导入的Component类。 每个Preact组件都需要扩展Component类。

    App定义了render方法,该方法返回一堆HTML元素和Preact组件,以渲染应用程序的主用户界面。

    div元素内部,我们有两个Preact组件: Header (呈现应用程序的标头)和Router组件。

    Preact Router与最新版本的React Router(版本4)相似。 您只需要用<Router>组件包装子组件,然后为每个组件指定path属性。 然后,路由器将负责呈现组件,该组件的路径属性与当前浏览器的URL相匹配。

    值得一提的是,Preact Router非常简单,与React Router不同,它不支持嵌套路由和视图组合之类的高级功能。 如果需要这些功能,则必须使用别名为preact-compat的React Router v3,或者最好使用比v3更强大且不需要任何兼容性层的最新React Router(版本4)。直接与Preact一起使用。 (有关示例,请参见此CodePen演示。)

    预先兼容层

    preact-compat模块允许开发人员从React切换到Preact,而无需将从React和ReactDOM的导入更改为Preact,或将现有的React软件包与Preact一起使用。

    使用preact-compat很容易。 您要做的就是首先通过npm安装它:

    npm i -S preact preact-compat

    然后设置您的构建系统以重定向导入,或将reactreact-dom要求重定向到preact-compat 例如,对于Webpack,您只需要向webpack.config.js添加以下配置:

    {
      "resolve": {
        "alias": {
          "react": "preact-compat",
          "react-dom": "preact-compat"
        }
      }
    }

    结论

    Preact是React的不错选择。 它的社区正在稳定增长,越来越多的Web应用程序正在使用它。 因此,如果您要构建具有高性能要求的Web应用程序,或者为速度较慢的2G网络构建移动应用程序,则应考虑使用Preact-作为您项目的第一个候选视图库,或作为React的直接替代品。

    翻译自: https://www.sitepoint.com/using-preact-react-alternative/

    展开全文
  • 就像React许多其他类似的库一样, Preact是虚拟DOM组件范例的实现。 与React不同,它只有3KB的大小,并且在速度方面也胜过它。
  • Gitalk评论配置-详解

    2019-09-07 20:55:04
    Gitalk 是一个基于 Github Issue Preact 开发的评论插件。为什么我要写这篇博文,是因我用Github搭建我的博客页面,在安装Gitalk插件时按网上文章介绍进行配置,在运行时总是出现错误: 每篇文章都只突出解释...
  • 在webpack-course项目下新建子文件src/main.css,然后随便写入某一个样式 touch src/main.css ...这是因为webpack仅能识别js文件,如果需要加载css那么我们需要通过加载器的形式来加载,那么我们在项目中安装
  • Next.js 是一个轻量级的 React 服务端渲染应用框架。 Next.js中文站点 http://nextjs.frontendx.cn ...当前翻译版本为 7.0.0-canary.8。 ... 安装 代码自动分割 CSS 支持嵌入样式 内嵌样...
  • 前言:译者一直使用react,...(更新到react16的MIT协议也不行)于是,笔者决定将react替换为preact,这样就需要在webpack配置中设置alias,但是又不希望使用create-react-app中不可逆的eject。于是找到了这篇教程...
  • webpack配置选项的设置

    2017-11-24 10:32:13
    webpack接受多种编程数据语言编写的配置文件。 1.TypeScript 使用TypeScript编写webpack的配置文件,必须先安装相关依赖: 命令行npm进行安装: npm install --save-dev typescript ts-node @types/node @types/...
  • title: Preact:Into the void 0(译)date: 2017-09-04 19:00:00 tags: [Preact, JSX, 虚拟DOM, 性能] 本文整理自Jason Miller在JSConf上的talk。原视频地址: https://www.youtube.com/watch?v=LY6y3HbDVmg ...
  • 本文整理自Jason Miller在JSConf上的talk。原视频地址: https://www.youtube.com/watch?v=LY6y3HbDVmg 开场白 嗨,大家好,我是Jason,Github上那个developit推特上的_developit,是一系列库的作者...
  • kbone 是一个致力于微信小程序 Web 端同构的解决方案。 简介 微信小程序的底层模型 Web 端不同,我们想直接把 Web 端的代码挪到小程序环境内执行是不可能的。kbone 的诞生就是为了解决这个问题,它实现了一个...
  • 文章目录安装步骤测试编写博客使用别人的主题购买域名备案购买云服务器部署博客引入评论系统引入百度统计引入Latex公式支持issues 安装步骤 jekyll的环境在本地电脑安装就可以了,不需要在部署博客的服务器上安装...
  • webpack 接受以多种编程数据语言编写的配置文件。支持的文件扩展名列表,可以在node-interpret包中找到。使用node-interpret,webpack 可以处理许多不同类型的配置文件。 TypeScript 为了用TypeScript书写 ...
  • 首先需要查看你的Bable版本,不同的版本有各自的对应配置。当然,出现这个错误的原因是因为我参照网上的教程,npm install babel-plugin-transform-decorators-legacy --save-dev,运行后就出现了错误。究其原因吗,...
  • 什么是Ant Design Mobile 一个基于 Preact / React / React Native 的 UI 组件库,详见官网:https://mobile.ant.design/index-cn 设置项目按需加载、定制...注意:工具会自动初始化一个脚手架并安装 React 项目的...
  • 安装项目

    2019-10-08 13:22:58
    学习掌握使用Mask R-CNN图像实例分割来训练自己的数据集 学习labelme图像实例分割标注工具 掌握多类物体的图像实例分割方法
  • 这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、...
  • 最近在看目标跟踪方面的论文,发现效果比较好的CNN也就是Siamese系列的算法了,可喜的就是商汤开源的pysot,里面实现了SiamRPN++SiameseMask,可以训练测试,还可以测 benchmark 代码很全面! ***pysot***的...
1 2 3 4 5 ... 13
收藏数 241
精华内容 96
关键字:

preact的安装和配置