1. 程式人生 > >從0到1寫一個react native的app(上)

從0到1寫一個react native的app(上)

我是一個react-native的初學者,在學習完react-native的一些基本內容,比如,頁面佈局,列表渲染,事件處理,網路請求,路由跳轉頁面等等之後,我想做一個實戰app來綜合應用所學的知識。下面是我要實現的app,一個簡單的github的app.


上圖是在genemotion模擬器的效果,本文將從0到1講解這個github APP的實現過程,希望對react-native的初學者有幫助,一些開發中遇到的坑我會和大家分享。前提是你已經搭建好rn的開發環境,我是使用window來開發rn。下面正式開始擼碼姿勢~

1.初始化專案

首先開啟一個目錄,初始化一個專案,這裡我的專案名是github,這可能會花比較長的時間,請耐心等待。。


當你的專案初始化完成後,進入github專案目錄,輸入react-native run-android,啟動專案(啟動專案之前先開啟的模擬器)。同樣,輸入這個命令後你需要等待一分鐘,這個時候你會看到一個 js server的視窗自動啟動


這裡給大家介紹一個坑,在開發過程中,當你的server視窗未關閉時,你npm第三方模組是不成功的,或者你在啟動server後在專案目錄中增加圖片,然後試圖在程式碼中引用你新新增的圖片時,js server也是會報錯的。這個時候你需要先關閉js server視窗,再進行安裝模組或者匯入圖片,再重新react-native run-android。

當你js server啟動成功,而且你的專案也build成功之後你會看到


這時候,看看你的模擬器,你會看到一個react-antive的初始應用。如下圖。


開啟你模擬器上的menu選單,開啟熱更新,這樣你更改你的程式碼,就可以實時再你的模擬器上看到效果。

下面,我將簡單的介紹一下這個初始程式碼,如果你已經掌握了rn的基礎,請直接跳過這裡。

左邊是專案目錄,你先在只需要關注index.android.js和index.ios.js兩個檔案,因為這是專案的入口檔案。現在開啟你的index.anddroid.js檔案

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

export default class github extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.android.js
        </Text>
        <Text style={styles.instructions}>
          Double tap R on your keyboard to reload,{'\n'}
          Shake or press menu button for dev menu
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

AppRegistry.registerComponent('github', () => github);

可以看到react-native的寫法和react是一樣的,元件仍然使用state作為元件的狀態管理,用props接受引數,仍然是使用jsx語法,不同點主要有:①你使用的不再是html的標籤而是使用rn給你提供的原生元件,比如<Button/> <Image/>等等。②你在定義樣式的時候是通過StyleSheet.create的形式建立一個styles樣式物件,然後再元件裡面通過style屬性引入樣式即可。AppRegistry模組則是用來告知React Native哪一個元件被註冊為整個應用的根容器。AppRegistry.registerComponent('github', () => github)這裡用引號括起來的'HelloWorldApp'必須和你init建立的專案名一致。

2.新增底部導航

現在我們不想把所有程式碼冗雜在index.android.js入口檔案裡面,我希望在入口檔案裡面import我們的主頁元件。先新增一個js目錄。


現在把你的index.android.js改成

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

import HomePage from './js/pages/HomePage';

export default class github extends Component {
  render() {
    return (
      <HomePage/>
    );
  }
}

AppRegistry.registerComponent('github', () => github);
現在我們需要寫我們的HomePage元件(頁面)了,這個頁面包含一個底部導航欄,點選導航的圖示,可以切換至不同的元件。lets do it..
import React, { Component } from 'react';
import {
    StyleSheet,
    Text,
    View,
    Image
} from 'react-native';

import TabNavigator from 'react-native-tab-navigator'

export default class HomePage extends Component {
    constructor(props){
        super(props);
        this.state = {
            selectedTab :'popular'
        }
    }
    render() {
        return (
            <View style={styles.container}>
                <TabNavigator>
                    <TabNavigator.Item
                        selected={this.state.selectedTab === 'popular'}
                        title="最熱"
                        selectedTitleStyle={{color: '#63B8FF'}}
                        renderIcon={() =>
                            <Image style={styles.icon} source={require('../../res/images/ic_popular.png')}/>}
                        renderSelectedIcon={() =>
                            <Image style={[styles.icon,,{tintColor:'#63B8FF'}]} source={require('../../res/images/ic_popular.png')}/>}
                        onPress={() => this.setState({selectedTab: 'popular'})}
                    >
                        {/*選項卡對應的頁面*/}
                        <View style={{backgroundColor:'pink',flex:1}}></View>
                    </TabNavigator.Item>

                    <TabNavigator.Item
                        selected={this.state.selectedTab === 'trending'}
                        title="趨勢"
                        selectedTitleStyle={{color: '#63B8FF'}}
                        renderIcon={() =>
                            <Image style={styles.icon} source={require('../../res/images/ic_trending.png')}/>}
                        renderSelectedIcon={() =>
                            <Image style={[styles.icon,{tintColor:'#63B8FF'}]} source={require('../../res/images/ic_trending.png')}/>}
                        onPress={() => this.setState({selectedTab: 'trending'})}
                    >
                        <View style={{backgroundColor:'lightblue',flex:1}}></View>
                    </TabNavigator.Item>

                    <TabNavigator.Item
                        selected={this.state.selectedTab === 'favorite'}
                        title="收藏"
                        selectedTitleStyle={{color: '#63B8FF'}}
                        renderIcon={() =>
                            <Image style={styles.icon} source={require('../../res/images/ic_favorite.png')}/>}
                        renderSelectedIcon={() =>
                            <Image style={[styles.icon,{tintColor:'#63B8FF'}]} source={require('../../res/images/ic_favorite.png')}/>}
                        onPress={() => this.setState({selectedTab: 'favorite'})}
                    >
                        <View style={{backgroundColor:'green',flex:1}}></View>
                    </TabNavigator.Item>

                    <TabNavigator.Item
                        selected={this.state.selectedTab === 'my'}
                        title="我的"
                        selectedTitleStyle={{color: '#63B8FF'}}
                        renderIcon={() =>
                            <Image style={styles.icon} source={require('../../res/images/ic_my.png')}/>}
                        renderSelectedIcon={() =>
                            <Image style={[styles.icon,{tintColor:'#63B8FF'}]} source={require('../../res/images/ic_my.png')}/>}
                        onPress={() => this.setState({selectedTab: 'my'})}
                    >
                        <View style={{backgroundColor:'white',flex:1}}></View>
                    </TabNavigator.Item>
                </TabNavigator>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1
    },
    icon: {
        width:26,
        height:26
    }
});
zh
這裡,我們用到了一個第三方元件react-native-tab-navigator,注意當TabNavigator.Item 的onpress新增點選點選事件時,依賴於HomePage元件的一個selectedTab狀態。這裡這個元件的使用方式挺簡單的,但是需要考慮一點的是每個tab對應一個元件,這些元件是一次性載入,還是當對應的item被點選時才惰性載入?當切換tab時,已經加瀏覽過的tab頁面是會儲存原來的狀態,還是重新載入?當然react-native-tab-navigator已經幫我們做了這些效能優化的問題。不過,你可以考慮,如果讓你實現這個元件,你會怎麼實現?還要說明一點是Image圖片的style樣式的tintColor是改變圖片的顏色。

最終實現的效果如下:

3.寫PopularPage頁面

現在我希望當我點選底部導航最熱圖示時,能夠顯示PopularPage頁面。所以在js/pages目錄下新建PopularPage.js元件。並且在HomePage頁面裡面匯入該元件,並替換我們前面的示意view.如下圖所示修改HomePage



接下來,讓我們完成PopularPage頁面。

我們的PopularPage頁面包含頭部和內容部分,如圖,我們要實現的效果圖如下


考慮到頁面頭部在很多頁面都要使用到,所以我要把它做成一個可配置的元件。在js/components下面新建NavigationBar.js,對於NavigationBar的中間的標題和左邊的元素,右邊的元素都應該作為配置項,通過props獲得的。

所以,Navigation Bar的參考程式碼如下

import React, {Component, PropTypes} from 'react';
import {
    StyleSheet,
    Text,
    View,
    StatusBar,
    Platform
} from 'react-native';

//會包含狀態列,還有頂部導航欄
export default class NavigationBar extends Component{
    static propTypes = {
        rightButton: PropTypes.element,
        leftButton: PropTypes.element
    }
    render(){
        return <View style={styles.container}>
            <View style={styles.statusBar}>
                <StatusBar hidden={false} barStyle="light-content"/>
            </View>
            {/*頂部導航欄*/}
            <View style={styles.navBar}>

                <View style={styles.leftBtnStyle}>
                    {this.props.leftButton}
                </View>
                <View style={styles.titleWrapper}>
                    <Text style={styles.title}>{this.props.title}</Text>
                </View>
                <View style={styles.rightBtn}>
                    {this.props.rightButton}
                </View>
            </View>
        </View>;
    }
}

const styles = StyleSheet.create({
    container: {
        backgroundColor:'#63B8FF',
        padding:5
    },
    statusBar:{
        height:Platform.OS === 'ios' ? 20 : 0
    },
    navBar:{
        flexDirection:'row',
        justifyContent:'space-between',
        alignItems:'center'
    },
    titleWrapper:{
        flexDirection:'column',
        justifyContent:'center',
        alignItems:'center',
        position:'absolute',
        left:40,
        right:40,
        bottom:0
    },
    title:{
        fontSize:16,
        color:'#FFF'
    },
    leftBtnStyle:{
        width:24,
        height:24
    },
    rightBtn:{
        flexDirection:'row',
        alignItems:'center',
        paddingRight:8
    }
});

現在我們在PopularPage裡面引入我們剛剛寫的NavigationBar頭部,並傳入引數,標題是一個文字,右邊元素是兩個圖示。Popular Page程式碼如下

import React, {Component} from 'react';
import {
    StyleSheet,
    Text,
    View,
    TouchableOpacity,
    Image
} from 'react-native';
import ScrollableTabView from "react-native-scrollable-tab-view";

import NavigationBar from '../components/NavigationBar';

export default class PopularPage extends Component {
    constructor(props){
        super(props);
    }
    getNavRightBtn = ()=>{
        return <View style={{flexDirection:'row',alignItems:'center'}}>
            <TouchableOpacity
                activeOpacity={0.7}>
                <Image source={require('../../res/images/ic_search_white_48pt.png')} style={{width:24,height:24}}/>
            </TouchableOpacity>

            <TouchableOpacity
                activeOpacity={0.7}>
                <Image source={require('../../res/images/ic_more_vert_white_48pt.png')} style={{width:24,height:24}}/>
            </TouchableOpacity>
        </View>;
    }
    render() {
        return (
            <View style={styles.container}>
                <NavigationBar
                    title="熱門"
                    rightButton={this.getNavRightBtn()}/>
            </View>
        );
    }
}
const styles = StyleSheet.create({
    container: {
        flex: 1
    }
});
這個時候看看你效果,頭部元件是不是出現了~~~~~~~~~~~接下讓我們實現下面的內容部分,這又是個tab。我們需要
react-native-scrollable-tab-view這個第三方元件。這個用法也很簡單。 

當然每個tab頁面,我封裝成另一個元件PopularTab。內容的類別我用元件的一個languages狀態儲存,每個tab頁面是一個滾動的列表用來展示從github伺服器上獲得的資料,
我用另一個PopularTab元件封裝,現在這個元件只顯示一句話。
import React, {Component} from 'react';
import {
    StyleSheet,
    Text,
    View,
    TouchableOpacity,
    Image
} from 'react-native';
import ScrollableTabView from "react-native-scrollable-tab-view";

import NavigationBar from '../components/NavigationBar';

export default class PopularPage extends Component {
    constructor(props){
        super(props);
        this.state = {
            languages: ['ios','android','js','react']
        };
    }
    getNavRightBtn = ()=>{
        return <View style={{flexDirection:'row',alignItems:'center'}}>
            <TouchableOpacity
                activeOpacity={0.7}>
                <Image source={require('../../res/images/ic_search_white_48pt.png')} style={{width:24,height:24}}/>
            </TouchableOpacity>

            <TouchableOpacity
                activeOpacity={0.7}>
                <Image source={require('../../res/images/ic_more_vert_white_48pt.png')} style={{width:24,height:24}}/>
            </TouchableOpacity>
        </View>;
    }
    render() {
        return (
            <View style={styles.container}>
                <NavigationBar
                    title="熱門"
                    rightButton={this.getNavRightBtn()}/>
                <ScrollableTabView
                    tabBarBackgroundColor="#63B8FF"
                    tabBarActiveTextColor="#FFF"
                    tabBarInactiveTextColor="#F5FFFA"
                    tabBarUnderlineStyle={{backgroundColor:"#E7E7E7",height:2}}>
                    {
                        this.state.languages.map((item,i)=>{
                            return <PopularTab key={`tab${i}`} tabLabel={item}/> ;
                        })
                    }
                </ScrollableTabView>
            </View>
        );
    }
}
class PopularTab extends Component{
    static defaultProps = {
        tabLabel: 'IOS',
    }
    render(){
        return(
            <View>
                <Text>{`這是${this.props.tabLabel}的內容`}</Text>
            </View>
        )
 }
}
const styles = StyleSheet.create({
    container: {
        flex: 1
    }
});
然後,你會得到如下效果~

4.使用fecth API獲取資料

React Native提供了和web標準一致的fecth API,基於ajax,但是採用promise的寫法。現在我們要從github伺服器上獲取當前選中selectedTab的最熱專案,程式碼如下:
       //載入資料 
        fetch(`https://api.github.com/search/repositories?q=${this.props.tabLabel}&sort=stars`)
            .then(response => response.json()) //伺服器響應response物件,繼續變成json物件
            .then(json => {
                //資料返回成功後執行回撥函式
            }).catch((error) => {
            console.error(error);
        }).done();
    
你可以在chrome瀏覽器中測試上述程式碼~新版的chrome是支援fetch API的。資料一共有192078條,而且每一項資料是一個json物件,觀察json資料都返回了哪些資訊。

5.使用List View高效渲染列表

渲染列表可以用Scroll View和ListView,兩者都是滾動事件的響應者,都可以渲染長列表。使用方法可以參考rn官網文件

ScrollView和ListView/FlatList應該如何選擇?ScrollView會簡單粗暴地把所有子元素一次性全部渲染出來。其原理淺顯易懂,使用上自然也最簡單。然而這樣簡單的渲染邏輯自然帶來了效能上的不足。想象一下你有一個特別長的列表需要顯示,可能有好幾屏的高度。建立和渲染那些螢幕以外的JS元件和原生檢視,顯然對於渲染效能和記憶體佔用都是一種極大的拖累和浪費。

這就是為什麼我們還有專門的ListView元件。ListView會惰性渲染子元素,只在它們將要出現在螢幕中時開始渲染。這種惰性渲染邏輯要複雜很多,因而API在使用上也更為繁瑣。除非你要渲染的資料特別少,否則你都應該儘量使用ListView,哪怕它們用起來更麻煩。

回到專案中來,現在改造我們的PopularTab元件,因為PopularTab元件是為了渲染github專案資料的. ListView元件的使用方式很簡單,只需要傳遞dataSource 和 renderRow的渲染方法,renderRow的引數是DataSource的其中一項。
<ListView
dataSource={this.state.dataSource}renderRow={this.renderRow}/>
現在,我的PopularTab程式碼如下
class PopularTab extends Component{
    static defaultProps = {
        tabLabel: 'IOS',
    }
    constructor(props){
        super(props);
        this.state = {
            dataSource : new ListView.DataSource({rowHasChanged:(r1,r2) => r1 !== r2}), //是一個優化,節省無用的UI渲染
        };
    }
    //渲染ListView的每一行
    renderRow = (obj)=>{
        return (<Text>{obj.name}</Text>);
    }
    //載入資料
    loadData = ()=>{
        this.setState({isLoading:true});
        //請求網路
        fetch(`https://api.github.com/search/repositories?q=${this.props.tabLabel}&sort=stars`)
            .then(response => response.json()) //伺服器響應response物件,繼續變成json物件
            .then(json => {
                console.log(json);
                //更新dataSource
                this.setState({
                    dataSource:this.state.dataSource.cloneWithRows(json.items)
                });
            }).catch((error) => {
            console.error(error);
        }).done();
    }
    render(){
        return(
            <View style={styles.container}>
                <ListView
                    dataSource={this.state.dataSource}
                    renderRow={this.renderRow}
                />
            </View>
        );
   }
    componentDidMount = ()=>{
        this.loadData();
    }
}
在我們非同步請求資料後回撥函式中,更新dataSource,觸發元件的render行為。
現在我們可以看到PopularPage頁面能夠渲染我們返回的資料了。
但是,不幸的是,顯示的很醜,而且在資料沒有返回前,沒有提示使用者正在載入資料,所以體驗很差,不過別急我們一個個擊破~~~~~

6.使用RefreshControl提示正在載入資訊,提高使用者體驗

還好,ScrollView和List View都有一個refreshControl屬性,用來指定當資料正在載入時顯示的元件~而rn原生元件中給我們提供了一個叫RefreshControl的元件, 修改PopularTab元件
<ListView
                    dataSource={this.state.dataSource}
                    renderRow={this.renderRow}
                    refreshControl={
                        <RefreshControl
                             refreshing={this.state.isLoading}
                            tintColor="#63B8FF"
                            title="正在載入..."
                            titleColor="#63B8FF"
                            colors={['#63B8FF']}/>}
                />
很顯然,你需要給PopularTab元件加一個loading狀態,當資料返回前為true,資料返回後設置為false。此時再看看效果吧,我這裡就不貼圖了

7.寫ProjectRow元件顯示專案

import React, {Component} from 'react';
import {
    StyleSheet,
    Text,
    View,
    Image,
    TouchableOpacity
} from 'react-native';

export default class ProjectRow extends Component{
    static defaultProps = {
        item: {}
    }
    render(){
        var item = this.props.item;
        return <TouchableOpacity
                onPress = {this.props.onSelect}
                activeOpacity={0.5}>
            <View style={styles.container}>
                <Text style={styles.title}>{item.full_name}</Text>
                <Text style={styles.description}>{item.description}</Text>
                <View style={styles.bottom}>
                    <View style={styles.bottomTextWrapper}>
                        <Text>作者:</Text>
                        <Image style={{width:22,height:22}} source={{uri:item.owner.avatar_url}}/>
                    </View>
                    <View style={styles.bottomTextWrapper}>
                        <Text>星:</Text>
                        <Text>{item.stargazers_count}</Text>
                    </View>
                    <Image source={require("../../res/images/ic_unstar_transparent.png")} style={{width:22,height:22}}/>
                </View>
            </View>
            </TouchableOpacity>
    }
}

const styles = StyleSheet.create({
    container: {
        flex:1,
        backgroundColor:'#FFF',
        flexDirection:'column',
        padding:10,
        marginLeft:5,
        marginRight:5,
        marginVertical:5,
        borderColor:'#dddddd',
        borderWidth:0.5,
        borderRadius:2,
        shadowColor:'gray',
        shadowOffset:{width:0.5,height:0.5},
        shadowRadius:1, //陰影半徑
        shadowOpacity:0.4,
        elevation:2 //Android 投影
    },
    title:{
        fontSize:16,
        marginBottom:2,
        color:'#212121'
    },
    description:{
        fontSize:14,
        marginBottom:2,
        color:'#757575'
    },
    bottom:{
        flexDirection:'row',
        alignItems:'center',
        justifyContent:'space-between'
    },
    bottomTextWrapper:{
        flexDirection:'row',
        alignItems:'center'
    }

});
這裡的UI你可以發揮你自己的藝術細胞進行改寫~~~~然後,我們原來每一個專案只是以Text的形式渲染專案名稱,現在我希望渲染標題,作者,star,圖片等等資訊,也就是每一項以ProjectRow元件的方式渲染~ 現在引入ProjectRow,並修改Popular Tab元件
//渲染ListView的每一行
    renderRow = (obj)=>{
        return <ProjectRow item={obj}/>;
    }
現在我們來看看效果~~


有木有感覺很棒呢!當然你點選專案的時候,不能看到專案詳情,以及如果使用者只對js感興趣,只想看js相關的熱門專案,不想看到其他的,那是不是還得增加一個訂閱內容的功能,這些功能我們放到下一次~~~~~敬請期待吧,下次課我們將講到用路由跳轉頁面,本地儲存,以及在app裡面巢狀網頁,以及響應事件相關的知識點~~~~~如果你喜歡本文,就給個贊吧~



相關推薦

0到1一個react native的app()

我是一個react-native的初學者,在學習完react-native的一些基本內容,比如,頁面佈局,列表渲染,事件處理,網路請求,路由跳轉頁面等等之後,我想做一個實戰app來綜合應用所學的知識。下面是我要實現的app,一個簡單的github的app.

一起學習造輪子(三):零開始一個React-Redux

導致 href dispatch 判斷 som render connect mis 回調 本文是一起學習造輪子系列的第三篇,本篇我們將從零開始寫一個React-Redux,本系列文章將會選取一些前端比較經典的輪子進行源碼分析,並且從零開始逐步實現,本系列將會學習Prom

一個Java WEB框架(七)Controller層轉換器

該系列,其實是對《架構探險》這本書的實踐。本人想記錄自己的學習心得所寫下的。 從一個簡單的Servlet專案開始起步。對每一層進行優化,然後形成一個輕量級的框架。 每一篇,都是針對專案的不足點進行優化的。 專案已放上github

一個Java WEB框架(六)Controller層優化

該系列,其實是對《架構探險》這本書的實踐。本人想記錄自己的學習心得所寫下的。 從一個簡單的Servlet專案開始起步。對每一層進行優化,然後形成一個輕量級的框架。 每一篇,都是針對專案的不足點進行優化的。 專案已放上github

一個Java WEB框架(五)IOC建立

該系列,其實是對《架構探險》這本書的實踐。本人想記錄自己的學習心得所寫下的。 從一個簡單的Servlet專案開始起步。對每一層進行優化,然後形成一個輕量級的框架。 每一篇,都是針對專案的不足點進行優化的。 專案已放上github

一個Java WEB框架(四)框架的演進

該系列,其實是對《架構探險》這本書的實踐。本人想記錄自己的學習心得所寫下的。 從一個簡單的Servlet專案開始起步。對每一層進行優化,然後形成一個輕量級的框架。 每一篇,都是針對專案的不足點進行優化的。 專案已放上github

一個Java WEB框架(三)Dao層優化

該系列,其實是對《架構探險》這本書的實踐。本人想記錄自己的學習心得所寫下的。 從一個簡單的Servlet專案開始起步。對每一層進行優化,然後形成一個輕量級的框架。 每一篇,都是針對專案的不足點進行優化的。 專案已放上github

一個Java WEB框架(二)Server層 優化

該系列,其實是對《架構探險》這本書的實踐。本人想記錄自己的學習心得所寫下的。 從一個簡單的Servlet專案開始起步。對每一層進行優化,然後形成一個輕量級的框架。 每一篇,都是針對專案的不足點進行優化的。 專案已放上github 上一篇地

一個Java WEB框架(一)

該系列,其實是對《架構探險》這本書的實踐。本人想記錄自己的學習心得所寫下的。 從一個簡單的Servlet專案開始起步。對每一層進行優化,然後形成一個輕量級的框架。 每一篇,都是針對專案的不足點進行優化的。 專案已放上github

用vue一個商城的貨元件(簡單易懂版,50行js實現效果)

0、結果放前面 加個Star後,fork下來。 然後在控制檯,先輸入npm install安裝依賴,再輸入npm run dev執行檢視效果 1、先列需求 一切開發都是基於需求做的,所以需求先行,根據需求設計功能。 需求如下: 上貨商品有多

一個編譯器(五):語法分析之自動機的缺陷和改進

專案的完整程式碼在 C2j-Compiler 前言 在上一篇,已經成功的構建了有限狀態自動機,但是這個自動機還存在兩個問題: 無法處理shift/reduce矛盾 狀態節點太多,導致自動機過大,效率較低 這一節就要解決這兩個問題 shift/reduce矛盾 看上一節那個例子的一個節點 e ->

一個編譯器(六):語法分析之表驅動語法分析

專案的完整程式碼在 C2j-Compiler 前言 上一篇已經正式的完成了有限狀態自動機的構建和足夠判斷reduce的資訊,接下來的任務就是根據這個有限狀態自動機來完成語法分析表和根據這個表來實現語法分析 reduce資訊 在完成語法分析表之前,還差最後一個任務,那就是描述reduce資訊,來指導自動機是

一個編譯器(七):語義分析之符號表的資料結構

專案的完整程式碼在 C2j-Compiler 前言 有關符號表的檔案都在symboltable包裡 前面我們通過完成一個LALR(1)有限狀態自動機和一個reduce資訊來構建了一個語法解析表,正式完成了C語言的語法解析。接下來就是進入語義分析部分,和在第二篇提到的一樣,語義分析的主要任務就是生成符號

一個編譯器(八):語義分析之構造符號表

專案的完整程式碼在 C2j-Compiler 前言 在之前完成了描述符號表的資料結構,現在就可以正式構造符號表了。符號表的建立自然是要根據語法分析過程中走的,所以符號表的建立就在LRStateTableParser裡的takeActionForReduce方法 不過在此之前,當然還需要一個方便對這個符號表

一個編譯器(九):語義分析之構造抽象語法樹(AST)

專案的完整程式碼在 C2j-Compiler 前言 在上一篇完成了符號表的構建,下一步就是輸出抽象語法樹(Abstract Syntax Tree,AST) 抽象語法樹(abstract syntax tree 或者縮寫為 AST),是原始碼的抽象語法結構的樹狀表現形式,這裡特指程式語言的原始碼。樹上的

一個編譯器(十):編譯前傳之直接解釋執行

專案的完整程式碼在 C2j-Compiler 前言 這一篇不看也不會影響後面程式碼生成部分 現在經過詞法分析語法分析語義分析,終於可以進入最核心的部分了。前面那部分可以稱作編譯器的前端,程式碼生成程式碼優化都是屬於編譯器後端,如今有關編譯器的工作崗位主要都是對後端的研究。當然現在寫的這個編譯器因為水平有限

一個編譯器(十一):程式碼生成之Java位元組碼基礎

專案的完整程式碼在 C2j-Compiler 前言 第十一篇,終於要進入程式碼生成部分了,但是但是在此之前,因為我們要做的是C語言到位元組碼的編譯,所以自然要了解一些位元組碼,但是由於C語言比較簡單,所以只需要瞭解一些位元組碼基礎 JVM的基本機制 JVM有一個執行環境叫做stack frame 這個

一個編譯器(十二):程式碼生成之生成邏輯

專案的完整程式碼在 C2j-Compiler 前言 在上一篇解釋完了一些基礎的Java位元組碼指令後,就可以正式進入真正的程式碼生成部分了。但是這部分先說的是程式碼生成依靠的幾個類,也就是用來生成指令的操作。 這一篇用到的檔案都在codegen下: Directive.java Instruction.

一個編譯器(十三):程式碼生成之遍歷AST

專案的完整程式碼在 C2j-Compiler 前言 在上一篇完成對JVM指令的生成,下面就可以真正進入程式碼生成部分了。通常現代編譯器都是先把生成IR,再經過程式碼優化等等,最後才編譯成目標平臺程式碼。但是時間水平有限,我們沒有IR也沒有程式碼優化,就直接利用AST生成Java位元組碼 入口 進行程式碼生

一個編譯器(完結):總結和系列索引

前言 這個系列算作我自己的學習筆記,到現在已經有十三篇了,加上這篇一共十四篇。一步一步的從詞法分析到語法分析、語義分析,再到程式碼生成,準備在這一篇做一個總結收尾和一個這個系列以前文章的索引。 (另外,由於我現在的這個主題不能對markdown的一級標題作目錄,所以這個系列文章的目錄都是有問題的) 索引 從零