欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談react 同構(gòu)之樣式直出

 更新時(shí)間:2017年11月07日 08:59:36   作者:devlee  
這篇文章主要介紹了淺談react 同構(gòu)之樣式直出,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

前言

上文講到通過(guò)同構(gòu)服務(wù)端渲染,可以直出html結(jié)構(gòu),雖然講解了樣式,圖片等靜態(tài)資源在服務(wù)端引入問(wèn)題的解決方案,但是并沒(méi)有實(shí)際進(jìn)行相關(guān)操作,這篇文章就講解一下如何讓樣式像html一樣直出。

PS: 直出,我的理解就是輸入url發(fā)起get請(qǐng)求訪問(wèn)服務(wù)端,直接得到完整響應(yīng)結(jié)果,而不是同過(guò)ajax異步去獲取。

React 同構(gòu)的關(guān)鍵要素

完善的 Compponent 屬性及生命周期與客戶端的 render 時(shí)機(jī)是 React 同構(gòu)的關(guān)鍵。

DOM 的一致性

在前后端渲染相同的 Compponent,將輸出一致的 Dom 結(jié)構(gòu)。

不同的生命周期

在服務(wù)端上 Component 生命周期只會(huì)到 componentWillMount,客戶端則是完整的。

客戶端 render 時(shí)機(jī)

同構(gòu)時(shí),服務(wù)端結(jié)合數(shù)據(jù)將 Component 渲染成完整的 HTML 字符串并將數(shù)據(jù)狀態(tài)返回給客戶端,客戶端會(huì)判斷是否可以直接使用或需要重新掛載。

以上便是 React 在同構(gòu)/服務(wù)端渲染的提供的基礎(chǔ)條件。在實(shí)際項(xiàng)目應(yīng)用中,還需要考慮其他邊角問(wèn)題,例如服務(wù)器端沒(méi)有 window 對(duì)象,需要做不同處理等。下面將通過(guò)在手Q家校群上的具體實(shí)踐,分享一些同構(gòu)的 Tips 及優(yōu)化成果

加入樣式文件

目前我們的項(xiàng)目中還不存在任何樣式文件,所以需要先寫(xiě)一個(gè),就給組件App寫(xiě)一個(gè)樣式文件吧。

安裝依賴

下面這些依賴都是后續(xù)會(huì)用到的,先安裝一下,下面會(huì)詳細(xì)講解每個(gè)依賴的作用。

復(fù)制代碼 代碼如下:

npm install postcss-loader postcss-import postcss-cssnext postcss-nested postcss-functions css-loader style-loader isomorphic-style-loader --save-dev

創(chuàng)建.pcss文件

css文件的后綴是.css,less文件的后綴是.less,這里我選擇使用PostCSS配合其插件來(lái)寫(xiě)樣式,所以我就自己定義一個(gè)后綴.pcss好了。

// ./src/client/component/app/style.pcss

.root {
 color: red;
}

設(shè)定一個(gè)root類(lèi),樣式就是簡(jiǎn)單的設(shè)置顏色為紅色。然后在App組件里引用它。

// ./src/client/component/app/index.tsx

...
import * as styles from './style.pcss';
...
 public render() {
  return (
   <div className={styles.root}>hello world</div>
  );
 }
...

這個(gè)時(shí)候你會(huì)發(fā)現(xiàn)編輯器里是這樣的:

出現(xiàn)這個(gè)問(wèn)題是因?yàn)閠s不知道這種模塊的類(lèi)型定義,所以我們需要手動(dòng)加入自定義模塊類(lèi)型定義。在項(xiàng)目根目錄下新建@types文件夾,在此目錄下建立index.d.ts文件:

// ./@types/index.d.ts

declare module '*.pcss' {
 const content: any;
 export = content;
}

保存之后就不會(huì)看到編輯器報(bào)錯(cuò)了,但是terminal里webpack打包會(huì)提示出錯(cuò),因?yàn)槲覀冞€沒(méi)有加對(duì)應(yīng)的loader。

配置.pcss文件的解析規(guī)則

js都組件化了,css模塊化也是很有必要的,不用再為避免取重復(fù)類(lèi)名而煩惱。我們?cè)赽ase配置里新導(dǎo)出一個(gè)方法用以獲取postcss的規(guī)則。

// ./src/webpack/base.ts

...
export const getPostCssRule = (styleLoader) => ({
 test: /\.pcss$/,
 use: [
  styleLoader,
  {
   loader: 'css-loader',
   options: {
    camelCase: true,
    importLoaders: 1,
    localIdentName: '[path][name]---[local]---[hash:base64:5]',
    modules: true,
   },
  },
  {
   loader: 'postcss-loader',
   options: {
    plugins: () => [
     require('postcss-import')({
      path: path.join(baseDir, './src/client/style'),
     }),
     require('postcss-cssnext'),
     require('postcss-nested'),
     require('postcss-functions')({
      functions: {
       x2(v, u) {
        return v * 2 + (u ? u : 'px');
       },
      },
     }),
    ],
   },
  },
 ],
});
...

我們可以從上面這個(gè)方法看到,要處理 .pcss 文件需要用到三個(gè)loader,按處理順序從下往上分別是postcss-loader, css-loader, 還有一個(gè)變量styleLoader,至于這個(gè)變量是什么,我們可以看使用到該方法的地方:

// ./src/webpack/client.ts

...
(clientDevConfig.module as webpack.NewModule).rules.push(
 ...
 getPostCssRule({
  loader: 'style-loader',
 }),
 ...
);
...

// ./src/webpack/server.ts

...
(clientDevConfig.module as webpack.NewModule).rules.push(
 ...
 getPostCssRule({
  loader: 'isomorphic-style-loader',
 }),
 ...
);
...

客戶端和服務(wù)端處理樣式文件需要使用到不同的styleLoader。

PostCSS簡(jiǎn)介

PostCSS是一個(gè)使用js來(lái)轉(zhuǎn)換css的工具,這個(gè)是官方介紹。其配合webpack使用的loader就是postcss-loader,但是只有單個(gè)postcss-loader其實(shí)沒(méi)有什么用,需要配合其插件來(lái)實(shí)現(xiàn)強(qiáng)大的功能。

1、postcss-import

這個(gè)插件我這里使用的原因是為了在樣式文件中@import時(shí)避免復(fù)雜的路徑編寫(xiě),我設(shè)定好path值,那么我在其它任何層級(jí)下的樣式文件中要引入path對(duì)應(yīng)文件夾里的公共變量樣式文件(假設(shè)叫"variables.pcss")時(shí)就非常方便,只需要寫(xiě)import 'variables.pcss';就可以了,當(dāng)然如果找不到對(duì)應(yīng)的文件,它會(huì)忽略path使用默認(rèn)相對(duì)路徑來(lái)查找。

2、postcss-cssnext

這個(gè)插件可以使用下一代css語(yǔ)法。

3、postcss-nested

這個(gè)插件可以嵌套編寫(xiě)樣式。

4、postcss-functions

這個(gè)插件可以自定義函數(shù),并在樣式文件中調(diào)用。

講這么多,寫(xiě)代碼舉個(gè)栗子吧~

我們?cè)赾lient目錄下新增style文件夾,用于存放一些樣式reset,變量文件之類(lèi)的東西。然后創(chuàng)建兩個(gè)pcss文件:

// ./src/client/style/variables.pcss

:root {
 --fontSizeValue: 16;
}
// ./src/client/style/index.pcss

@import 'variables.pcss';

body {
 margin: 0;
 font-size: x2(var(--fontSizeValue));
}

引入我們剛寫(xiě)的index.pcss

// ./src/client/index.tsx
...
import './style/index.pcss';
...

CSS Modules簡(jiǎn)介

簡(jiǎn)單來(lái)說(shuō)就是css模塊化,不用再擔(dān)心全局類(lèi)名的問(wèn)題。我們根據(jù)上述css-loader的options來(lái)看:

  1. camelCase為true運(yùn)行使用駝峰寫(xiě)法來(lái)寫(xiě)類(lèi)名
  2. importLoaders的值為N是因?yàn)樵赾ss-loader之前有N個(gè)loader已經(jīng)處理過(guò)文件了,這里的N值是1,因?yàn)橹坝幸粋€(gè)postcss-loader,這個(gè)值一定要設(shè)置對(duì),否則會(huì)影響@import語(yǔ)句,我的這個(gè)表述可能不是太正確,詳細(xì)可參見(jiàn) Clarify importLoaders documentation? 這個(gè)地方詳細(xì)講解了,我翻譯一下大概意思是,這個(gè)屬性的值N代表的是對(duì)于@import的文件要經(jīng)過(guò)css-loader后面的N個(gè)loader的處理,英文不太好,大家可以自行理解。
  3. localIdentName這個(gè)就是指生成的類(lèi)名啦,具體看后續(xù)結(jié)果截圖就一目了然了。
  4. modules為true即啟用模塊化

isomorphic-style-loader

在客戶端,使用style-loader,它會(huì)動(dòng)態(tài)的往dom里插入style元素,而服務(wù)端由于缺少客戶端的相關(guān)對(duì)象及API,所以需要isomorphic-style-loader,目前用到它只是為了避免報(bào)錯(cuò)哈哈,后續(xù)還有大作用,樣式直出全靠它。

打包運(yùn)行

注意:打包運(yùn)行之前不要忘了給tsconfig.client.json和tsconfig.server.json引入我們的自定義模塊定義文件index.d.ts,不然webpack編譯就會(huì)報(bào)找不到pcss這種模塊啦。

// ./src/webpack/tsconfig.client(server).json
...
"include": [
  ...
  "../../@types/**/*",
  ...
]
...

運(yùn)行結(jié)果如下:

雖然style元素已經(jīng)存在,但是這個(gè)是由style-loader生成的,并不是服務(wù)端直出的,看page source就知道了。

而且在刷新頁(yè)面的時(shí)候能很明顯的看到樣式變化閃爍的效果。

直出樣式

我們利用isomorphic-style-loader來(lái)實(shí)現(xiàn)服務(wù)端直出樣式,原理的話根據(jù)官方介紹就是利用了react的context api來(lái)實(shí)現(xiàn),在服務(wù)端渲染的過(guò)程中,利用注入的insertCss方法和高階組件(hoc high-order component)來(lái)獲取樣式代碼。

安裝依賴

npm install prop-types --save-dev

改寫(xiě)App組件

根據(jù)其官方介紹,我們?cè)诓皇褂闷湔贤戤叺膇somorphic router的情況下,需要寫(xiě)一個(gè)Provider給App組件:

// ./src/client/component/app/provider.tsx

import * as React from 'react';

import * as PropTypes from 'prop-types';

class AppProvider extends React.PureComponent<any, any> {
 public static propTypes = {
  context: PropTypes.object,
 };

 public static defaultProps = {
  context: {
   insertCss: () => '',
  },
 };

 public static childContextTypes = {
  insertCss: PropTypes.func.isRequired,
 };

 public getChildContext() {
  return this.props.context;
 }

 public render() {
  return this.props.children || null;
 }
}

export default AppProvider;

將原App組件里的具體內(nèi)容遷移到AppContent組件里去:

// ./src/client/component/app/content.tsx

import * as React from 'react';

import * as styles from './style.pcss';

/* tslint:disable-next-line no-submodule-imports */
import withStyles from 'isomorphic-style-loader/lib/withStyles';

@withStyles(styles)
class AppContent extends React.PureComponent {
 public render() {
  return (
   <div className={styles.root}>hello world</div>
  );
 }
}

export default AppContent;

新的App組件:

// ./src/client/component/app/index.tsx

import * as React from 'react';

import AppProvider from './provider';

import AppContent from './content';

class App extends React.PureComponent {
 public render() {
  return (
   <AppProvider>
    <AppContent />
   </AppProvider>
  );
 }
}

export default App;

疑問(wèn)一:AppProvider組件是做什么的?

答:Provider的意思是 供應(yīng)者,提供者 。顧名思義,AppProvider為其后代組件提供了一些東西,這個(gè)東西就是context,它有一個(gè)insertCss方法。根據(jù)其定義,該方法擁有默認(rèn)值,返回空字符串的函數(shù),即默認(rèn)沒(méi)什么作用,但是可以通過(guò)props傳入context來(lái)達(dá)到自定義的目的。通過(guò)設(shè)定childContextTypes和getChildContext,該組件后代凡是設(shè)定了contextTypes的組件都會(huì)擁有this.context對(duì)象,而這個(gè)對(duì)象正是getChildContext的返回值。

疑問(wèn)二:AppContent為何要獨(dú)立出去?

答:接上一疑問(wèn),AppProvider組件render其子組件,而要使得context這個(gè)api生效,其子組件必須是定義了contextTypes的,但是我們并沒(méi)有看見(jiàn)AppContent有這個(gè)定義,這個(gè)是因?yàn)檫@個(gè)定義在高階組件withStyles里面(參見(jiàn)其 源碼 )。

疑問(wèn)三:@withStyles是什么語(yǔ)法?

答:這個(gè)是裝飾器,屬于es7。使用該語(yǔ)法,需要配置tsconfig:

// ./tsconfig.json
// ./src/webpack/tsconfig.client(server).json

{
 ...
 "compilerOptions": {
  ...
  "experimentalDecorators": true,
  ...
 },
 ...
}

改寫(xiě)服務(wù)端bundle文件

由于App組件的改寫(xiě),服務(wù)端不能再?gòu)?fù)用該組件,但是AppProvider和AppContent目前還是可以復(fù)用的。

// ./src/server/bundle.tsx

import * as React from 'react';

/* tslint:disable-next-line no-submodule-imports */
import { renderToString } from 'react-dom/server';

import AppProvider from '../client/component/app/provider';

import AppContent from '../client/component/app/content';

export default {
 render() {
  const css = [];
  const context = { insertCss: (...styles) => styles.forEach((s) => css.push(s._getCss())) };
  const html = renderToString(
   <AppProvider context={context}>
    <AppContent />
   </AppProvider>,
  );
  const style = css.join('');
  return {
   html,
   style,
  };
 },
};

這里我們傳入了自定義的context對(duì)象,通過(guò)css這個(gè)變量來(lái)存儲(chǔ)style信息。我們?cè)萺ender函數(shù)直接返回renderToString的html字符串,而現(xiàn)在多了一個(gè)style,所以我們返回?fù)碛衕tml和style屬性的對(duì)象。

疑問(wèn)四:官方示例css是一個(gè)Set類(lèi)型實(shí)例,這里怎么是一個(gè)數(shù)組類(lèi)型實(shí)例?

答:Set是es6中新的數(shù)據(jù)結(jié)構(gòu),類(lèi)似數(shù)組,但可以保證無(wú)重復(fù)值,只有tsconfig的編譯選項(xiàng)中的target為es6時(shí),且加入es2017的lib時(shí)才不會(huì)報(bào)錯(cuò),由于我們的target是es5,所以是數(shù)組,且使用數(shù)組并沒(méi)有太大問(wèn)題。

處理服務(wù)端入口文件

由于bundle的render值變更,所以我們也要處理一下。

// ./src/server/index.tsx

...
router.get('/*', (ctx: Koa.Context, next) => { // 配置一個(gè)簡(jiǎn)單的get通配路由
 const renderResult = bundle ? bundle.render() : {}; // 獲得渲染出的結(jié)果對(duì)象
 const { html = '', style = '' } = renderResult;
 ...
 ctx.body = `
  ...
  <head>
   ...
   ${style ? `<style>${style}</style>` : ''}
   ...
  </head>
  ...
 `;
 ...
});
...

直出結(jié)果

樣式直出后的page source:

找回丟失的公共樣式文件

從上面的直出結(jié)果來(lái)看,缺少./src/style/index.pcss這個(gè)樣式代碼,原因顯而易見(jiàn),它不屬于任何一個(gè)組件,它是公共的,我們?cè)诳蛻舳巳肟谖募镆肓怂?。?duì)于公共樣式文件,服務(wù)端要直出這部分內(nèi)容,可以這么做:

./src/server/bundle.tsx

...
import * as commonStyles from '../client/style/index.pcss';
...
const css = [commonStyles._getCss()];
...

我們利用isomorphic-style-loader提供的api可以得到這部分樣式代碼字符串。這樣就可以得到完整的直出樣式了。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • VSCode搭建React Native環(huán)境

    VSCode搭建React Native環(huán)境

    這篇文章主要介紹了VSCode搭建React Native環(huán)境,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • React-Native之TextInput組件的設(shè)置以及如何獲取輸入框的內(nèi)容

    React-Native之TextInput組件的設(shè)置以及如何獲取輸入框的內(nèi)容

    這篇文章主要介紹了React-Native之TextInput組件的設(shè)置以及如何獲取輸入框的內(nèi)容問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • React?Immutable使用方法詳細(xì)介紹

    React?Immutable使用方法詳細(xì)介紹

    Immutable.js出自Facebook,是最流行的不可變數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)之一。它實(shí)現(xiàn)了完全的持久化數(shù)據(jù)結(jié)構(gòu),使用結(jié)構(gòu)共享。所有的更新操作都會(huì)返回新的值,但是在內(nèi)部結(jié)構(gòu)是共享的,來(lái)減少內(nèi)存占用
    2022-09-09
  • react?context優(yōu)化四重奏教程示例

    react?context優(yōu)化四重奏教程示例

    這篇文章主要為大家介紹了react?context優(yōu)化四重奏教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • React深入淺出分析Hooks源碼

    React深入淺出分析Hooks源碼

    在react類(lèi)組件(class)寫(xiě)法中,有setState和生命周期對(duì)狀態(tài)進(jìn)行管理,但是在函數(shù)組件中不存在這些,故引入hooks(版本:>=16.8),使開(kāi)發(fā)者在非class的情況下使用更多react特性
    2022-11-11
  • react-native 配置@符號(hào)絕對(duì)路徑配置和絕對(duì)路徑?jīng)]有提示的問(wèn)題

    react-native 配置@符號(hào)絕對(duì)路徑配置和絕對(duì)路徑?jīng)]有提示的問(wèn)題

    本文主要介紹了react-native 配置@符號(hào)絕對(duì)路徑配置和絕對(duì)路徑?jīng)]有提示的問(wèn)題,文中通過(guò)圖文示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-01-01
  • 在create-react-app中使用css modules的示例代碼

    在create-react-app中使用css modules的示例代碼

    這篇文章主要介紹了在create-react-app中使用css modules的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • 基于React.js實(shí)現(xiàn)簡(jiǎn)單的文字跑馬燈效果

    基于React.js實(shí)現(xiàn)簡(jiǎn)單的文字跑馬燈效果

    剛好手上有一個(gè)要實(shí)現(xiàn)文字跑馬燈的react項(xiàng)目,然后ant-design上面沒(méi)有這個(gè)組件,于是只能自己手?jǐn)]一個(gè),文中的實(shí)現(xiàn)方法講解詳細(xì),希望對(duì)大家有所幫助
    2023-01-01
  • React實(shí)現(xiàn)多標(biāo)簽在有限空間內(nèi)展示

    React實(shí)現(xiàn)多標(biāo)簽在有限空間內(nèi)展示

    在業(yè)務(wù)中,需要在一個(gè)卡片組件中展示多個(gè)標(biāo)簽,標(biāo)簽組件高度相同,寬度和出現(xiàn)順序不同,要求標(biāo)簽只能在有限的空間內(nèi)展示,所以本文給大家介紹了React實(shí)現(xiàn)多標(biāo)簽在有限空間內(nèi)展示,需要的朋友可以參考下
    2023-12-12
  • react-native滑動(dòng)吸頂效果的實(shí)現(xiàn)過(guò)程

    react-native滑動(dòng)吸頂效果的實(shí)現(xiàn)過(guò)程

    這篇文章主要給大家介紹了關(guān)于react-native滑動(dòng)吸頂效果的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用react-native具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06

最新評(píng)論